You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/03/14 11:35:10 UTC

[GitHub] [incubator-nuttx] jrosberg opened a new pull request #5740: Add ethernet support for risc-v/MPFS

jrosberg opened a new pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740


   ## Summary
   Add ethernet mac support  for MPFS SoC and Icicle board
   
   ## Impact
   New driver.
   dual mac support is untested and multi queue not implemented.
   
   ## Testing
   Tested on Icile board with TCP-blaster and telnet.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r828122019



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == (void *)MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == (void *)MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)(1UL)) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS (values from baremetal driver) */
+
+  mac_putreg(priv, PCS_CONTROL, 0x8000);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config =  (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                 DMA_CONFIG_TX_PBUF_SIZE |
+                 (((uint32_t)(0x3ul)) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                 ((uint32_t)(0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr;
+  r &= ~1;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif
+{
+  struct mpfs_ethmac_s *priv;
+  int ret = OK;
+  uintptr_t base;
+
+  ninfo("intf: %d\n", intf);
+
+  /* Get the interface structure associated with this interface number. */
+
+  DEBUGASSERT(intf < MPFS_NETHERNET);
+  priv = &g_mpfsethmac[intf];
+
+  /* Initialize the driver structure */
+
+  memset(priv, 0, sizeof(struct mpfs_ethmac_s));
+  priv->dev.d_buf     = g_pktbuf;       /* Single packet buffer */
+  priv->dev.d_ifup    = mpfs_ifup;      /* I/F up (new IP address) callback */
+  priv->dev.d_ifdown  = mpfs_ifdown;    /* I/F down callback */
+  priv->dev.d_txavail = mpfs_txavail;   /* New TX data callback */
+#ifdef CONFIG_NET_MCASTGROUP
+  priv->dev.d_addmac  = mpfs_addmac;    /* Add multicast MAC address */
+  priv->dev.d_rmmac   = mpfs_rmmac;     /* Remove multicast MAC address */
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+  priv->dev.d_ioctl   = mpfs_ioctl;     /* Support PHY ioctl() calls */
+#endif
+  priv->dev.d_private = priv;           /* Used to recover private state */
+  priv->intf          = intf;           /* Remember the interface number */
+  priv->regbase       = g_regbases[intf];
+  priv->mac_q_int[0]  = g_irq_numbers[intf][0];
+  priv->mac_q_int[1]  = g_irq_numbers[intf][1];
+  priv->mac_q_int[2]  = g_irq_numbers[intf][2];
+  priv->mac_q_int[3]  = g_irq_numbers[intf][3];
+  ninfo("mac @ 0x%" PRIx64 "\n", priv->regbase);
+
+  base = priv->regbase;
+  priv->queue[0].int_status     = (uint32_t *)(base + INT_STATUS);
+  priv->queue[1].int_status     = (uint32_t *)(base + INT_Q1_STATUS);
+  priv->queue[2].int_status     = (uint32_t *)(base + INT_Q2_STATUS);
+  priv->queue[3].int_status     = (uint32_t *)(base + INT_Q3_STATUS);
+  priv->queue[0].int_mask       = (uint32_t *)(base + INT_MASK);
+  priv->queue[1].int_mask       = (uint32_t *)(base + INT_Q1_MASK);
+  priv->queue[2].int_mask       = (uint32_t *)(base + INT_Q2_MASK);
+  priv->queue[3].int_mask       = (uint32_t *)(base + INT_Q3_MASK);
+  priv->queue[0].int_enable     = (uint32_t *)(base + INT_ENABLE);
+  priv->queue[1].int_enable     = (uint32_t *)(base + INT_Q1_ENABLE);
+  priv->queue[2].int_enable     = (uint32_t *)(base + INT_Q2_ENABLE);
+  priv->queue[3].int_enable     = (uint32_t *)(base + INT_Q3_ENABLE);
+  priv->queue[0].int_disable    = (uint32_t *)(base + INT_DISABLE);
+  priv->queue[1].int_disable    = (uint32_t *)(base + INT_Q1_DISABLE);
+  priv->queue[2].int_disable    = (uint32_t *)(base + INT_Q2_DISABLE);
+  priv->queue[3].int_disable    = (uint32_t *)(base + INT_Q3_DISABLE);
+  priv->queue[0].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q_PTR);
+  priv->queue[1].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q1_PTR);
+  priv->queue[2].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q2_PTR);
+  priv->queue[3].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q3_PTR);
+  priv->queue[0].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q_PTR);
+  priv->queue[1].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q1_PTR);
+  priv->queue[2].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q2_PTR);
+  priv->queue[3].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q3_PTR);
+  priv->queue[0].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q1);
+  priv->queue[1].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q1);
+  priv->queue[2].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q2);
+  priv->queue[3].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q3);
+
+  /* MPU hack for ETH DMA if not enabled by bootloader */
+
+#ifdef CONFIG_MPFS_MPU_DMA_ENABLE
+#  ifdef CONFIG_MPFS_ETHMAC_0
+  putreg64(0x1f00000fffffffff, MPFS_PMPCFG_ETH0_0);
+#  endif
+#  ifdef CONFIG_MPFS_ETHMAC_1
+  putreg64(0x1f00000fffffffff, MPFS_PMPCFG_ETH1_0);
+#  endif
+#endif
+
+  /* Allocate buffers */
+
+  ret = mpfs_buffer_initialize(priv, 0);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_buffer_initialize failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Attach the IRQ to the driver */
+
+  if (irq_attach(priv->mac_q_int[0], mpfs_interrupt_0, priv))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  if (irq_attach(priv->mac_q_int[1], mpfs_interrupt_1, NULL))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  if (irq_attach(priv->mac_q_int[2], mpfs_interrupt_2, NULL))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  if (irq_attach(priv->mac_q_int[3], mpfs_interrupt_3, NULL))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  /* Enable clocking to the GMAC peripheral (just for mpfs_ifdown()) */
+
+  /* MAC HW clock enable and reset */
+
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET, 0,
+                  SYSREG_SUBBLK_CLOCK_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+
+      for (volatile int i = 0; i < 10000; i++)
+        {
+          ;
+        }
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET, 0,
+                  SYSREG_SUBBLK_CLOCK_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+
+      for (volatile int i = 0; i < 10000; i++)
+        {
+          ;
+        }
+

Review comment:
       I'll remove this. it was attempt to get reset working on some weird cases. but has no effect actually.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r828169553



##########
File path: arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h
##########
@@ -0,0 +1,701 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H
+#define __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/mpfs_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets *********************************************************/
+
+#define NETWORK_CONTROL                     0x0000
+#define NETWORK_CONFIG                      0x0004
+#define NETWORK_STATUS                      0x0008
+#define DMA_CONFIG                          0x0010
+#define TRANSMIT_STATUS                     0x0014
+#define RECEIVE_Q_PTR                       0x0018
+#define TRANSMIT_Q_PTR                      0x001C
+#define RECEIVE_STATUS                      0x0020
+#define INT_STATUS                          0x0024
+#define INT_ENABLE                          0x0028
+#define INT_DISABLE                         0x002C
+#define INT_MASK                            0x0030
+#define PHY_MANAGEMENT                      0x0034
+#define PAUSE_TIME                          0x0038
+#define TX_PAUSE_QUANTUM                    0x003C
+#define PBUF_TXCUTTHRU                      0x0040
+#define PBUF_RXCUTTHRU                      0x0044
+#define JUMBO_MAX_LENGTH                    0x0048
+#define AXI_MAX_PIPELINE                    0x0054
+#define RSC_CONTROL                         0x0058
+#define INT_MODERATION                      0x005C
+#define SYS_WAKE_TIME                       0x0060
+#define LOCKUP_CONFIG                       0x0068
+#define MAC_LOCKUP_TIME                     0x006C
+#define LOCKUP_CONFIG3                      0x0070
+#define RX_WATER_MARK                       0x007C
+#define HASH_BOTTOM                         0x0080
+#define HASH_TOP                            0x0084
+#define SPEC_ADD1_BOTTOM                    0x0088
+#define SPEC_ADD1_TOP                       0x008C
+#define SPEC_ADD2_BOTTOM                    0x0090
+#define SPEC_ADD2_TOP                       0x0094
+#define SPEC_ADD3_BOTTOM                    0x0098
+#define SPEC_ADD3_TOP                       0x009C
+#define SPEC_ADD4_BOTTOM                    0x00A0
+#define SPEC_ADD4_TOP                       0x00A4
+#define SPEC_TYPE1                          0x00A8
+#define SPEC_TYPE2                          0x00AC
+#define SPEC_TYPE3                          0x00B0
+#define SPEC_TYPE4                          0x00B4
+#define WOL_REGISTER                        0x00B8
+#define STRETCH_RATIO                       0x00BC
+#define STACKED_VLAN                        0x00C0
+#define TX_PFC_PAUSE                        0x00C4
+#define MASK_ADD1_BOTTOM                    0x00C8
+#define MASK_ADD1_TOP                       0x00CC
+#define DMA_ADDR_OR_MASK                    0x00D0
+#define RX_PTP_UNICAST                      0x00D4
+#define TX_PTP_UNICAST                      0x00D8
+#define TSU_NSEC_CMP                        0x00DC
+#define TSU_SEC_CMP                         0x00E0
+#define TSU_MSB_SEC_CMP                     0x00E4
+#define TSU_PTP_TX_MSB_SEC                  0x00E8
+#define TSU_PTP_RX_MSB_SEC                  0x00EC
+#define TSU_PEER_TX_MSB_SEC                 0x00F0
+#define TSU_PEER_RX_MSB_SEC                 0x00F4
+#define DPRAM_FILL_DBG                      0x00F8
+#define REVISION_REG                        0x00FC
+#define OCTETS_TXED_BOTTOM                  0x0100
+#define OCTETS_TXED_TOP                     0x0104
+#define FRAMES_TXED_OK                      0x0108
+#define BROADCAST_TXED                      0x010C
+#define MULTICAST_TXED                      0x0110
+#define PAUSE_FRAMES_TXED                   0x0114
+#define FRAMES_TXED_64                      0x0118
+#define FRAMES_TXED_65                      0x011C
+#define FRAMES_TXED_128                     0x0120
+#define FRAMES_TXED_256                     0x0124
+#define FRAMES_TXED_512                     0x0128
+#define FRAMES_TXED_1024                    0x012C
+#define FRAMES_TXED_1519                    0x0130
+#define TX_UNDERRUNS                        0x0134
+#define SINGLE_COLLISIONS                   0x0138
+#define MULTIPLE_COLLISIONS                 0x013C
+#define EXCESSIVE_COLLISIONS                0x0140
+#define LATE_COLLISIONS                     0x0144
+#define DEFERRED_FRAMES                     0x0148
+#define CRS_ERRORS                          0x014C
+#define OCTETS_RXED_BOTTOM                  0x0150
+#define OCTETS_RXED_TOP                     0x0154
+#define FRAMES_RXED_OK                      0x0158
+#define BROADCAST_RXED                      0x015C
+#define MULTICAST_RXED                      0x0160
+#define PAUSE_FRAMES_RXED                   0x0164
+#define FRAMES_RXED_64                      0x0168
+#define FRAMES_RXED_65                      0x016C
+#define FRAMES_RXED_128                     0x0170
+#define FRAMES_RXED_256                     0x0174
+#define FRAMES_RXED_512                     0x0178
+#define FRAMES_RXED_1024                    0x017C
+#define FRAMES_RXED_1519                    0x0180
+#define UNDERSIZE_FRAMES                    0x0184
+#define EXCESSIVE_RX_LENGTH                 0x0188
+#define RX_JABBERS                          0x018C
+#define FCS_ERRORS                          0x0190
+#define RX_LENGTH_ERRORS                    0x0194
+#define RX_SYMBOL_ERRORS                    0x0198
+#define ALIGNMENT_ERRORS                    0x019C
+#define RX_RESOURCE_ERRORS                  0x01A0
+#define RX_OVERRUNS                         0x01A4
+#define RX_IP_CK_ERRORS                     0x01A8
+#define RX_TCP_CK_ERRORS                    0x01AC
+#define RX_UDP_CK_ERRORS                    0x01B0
+#define AUTO_FLUSHED_PKTS                   0x01B4
+#define TSU_TIMER_INCR_SUB_NSEC             0x01BC
+#define TSU_TIMER_MSB_SEC                   0x01C0
+#define TSU_STROBE_MSB_SEC                  0x01C4
+#define TSU_STROBE_SEC                      0x01C8
+#define TSU_STROBE_NSEC                     0x01CC
+#define TSU_TIMER_SEC                       0x01D0
+#define TSU_TIMER_NSEC                      0x01D4
+#define TSU_TIMER_ADJUST                    0x01D8
+#define TSU_TIMER_INCR                      0x01DC
+#define TSU_PTP_TX_SEC                      0x01E0
+#define TSU_PTP_TX_NSEC                     0x01E4
+#define TSU_PTP_RX_SEC                      0x01E8
+#define TSU_PTP_RX_NSEC                     0x01EC
+#define TSU_PEER_TX_SEC                     0x01F0
+#define TSU_PEER_TX_NSEC                    0x01F4
+#define TSU_PEER_RX_SEC                     0x01F8
+#define TSU_PEER_RX_NSEC                    0x01FC
+#define PCS_CONTROL                         0x0200
+#define PFC_STATUS                          0x026C
+#define RX_LPI                              0x0270
+#define RX_LPI_TIME                         0x0274
+#define TX_LPI                              0x0278
+#define TX_LPI_TIME                         0x027C
+#define DESIGNCFG_DEBUG1                    0x0280
+#define DESIGNCFG_DEBUG2                    0x0284
+#define DESIGNCFG_DEBUG3                    0x0288
+#define DESIGNCFG_DEBUG4                    0x028C
+#define DESIGNCFG_DEBUG5                    0x0290
+#define DESIGNCFG_DEBUG6                    0x0294
+#define DESIGNCFG_DEBUG7                    0x0298
+#define DESIGNCFG_DEBUG8                    0x029C
+#define DESIGNCFG_DEBUG9                    0x02A0
+#define DESIGNCFG_DEBUG10                   0x02A4
+#define DESIGNCFG_DEBUG11                   0x02A8
+#define DESIGNCFG_DEBUG12                   0x02AC
+#define AXI_QOS_CFG_0                       0x02E0
+#define AXI_QOS_CFG_1                       0x02E4
+#define AXI_QOS_CFG_2                       0x02E8
+#define AXI_QOS_CFG_3                       0x02EC
+#define INT_Q1_STATUS                       0x0400
+#define INT_Q2_STATUS                       0x0404
+#define INT_Q3_STATUS                       0x0408
+#define INT_Q4_STATUS                       0x040C
+#define INT_Q5_STATUS                       0x0410
+#define INT_Q6_STATUS                       0x0414
+#define INT_Q7_STATUS                       0x0418
+#define INT_Q8_STATUS                       0x041C
+#define INT_Q9_STATUS                       0x0420
+#define INT_Q10_STATUS                      0x0424
+#define INT_Q11_STATUS                      0x0428
+#define INT_Q12_STATUS                      0x042C
+#define INT_Q13_STATUS                      0x0430
+#define INT_Q14_STATUS                      0x0434
+#define INT_Q15_STATUS                      0x0438
+#define TRANSMIT_Q1_PTR                     0x0440
+#define TRANSMIT_Q2_PTR                     0x0444
+#define TRANSMIT_Q3_PTR                     0x0448
+#define TRANSMIT_Q4_PTR                     0x044C
+#define TRANSMIT_Q5_PTR                     0x0450
+#define TRANSMIT_Q6_PTR                     0x0454
+#define TRANSMIT_Q7_PTR                     0x0458
+#define TRANSMIT_Q8_PTR                     0x045C
+#define TRANSMIT_Q9_PTR                     0x0460
+#define TRANSMIT_Q10_PTR                    0x0464
+#define TRANSMIT_Q11_PTR                    0x0468
+#define TRANSMIT_Q12_PTR                    0x046C
+#define TRANSMIT_Q13_PTR                    0x0470
+#define TRANSMIT_Q14_PTR                    0x0474
+#define TRANSMIT_Q15_PTR                    0x0478
+#define RECEIVE_Q1_PTR                      0x0480
+#define RECEIVE_Q2_PTR                      0x0484
+#define RECEIVE_Q3_PTR                      0x0488
+#define RECEIVE_Q4_PTR                      0x048C
+#define RECEIVE_Q5_PTR                      0x0490
+#define RECEIVE_Q6_PTR                      0x0494
+#define RECEIVE_Q7_PTR                      0x0498
+#define DMA_RXBUF_SIZE_Q1                   0x04A0
+#define DMA_RXBUF_SIZE_Q2                   0x04A4
+#define DMA_RXBUF_SIZE_Q3                   0x04A8
+#define DMA_RXBUF_SIZE_Q4                   0x04AC
+#define DMA_RXBUF_SIZE_Q5                   0x04B0
+#define DMA_RXBUF_SIZE_Q6                   0x04B4
+#define DMA_RXBUF_SIZE_Q7                   0x04B8
+#define CBS_CONTROL                         0x04BC
+#define CBS_IDLESLOPE_Q_A                   0x04C0
+#define CBS_IDLESLOPE_Q_B                   0x04C4
+#define UPPER_TX_Q_BASE_ADDR                0x04C8
+#define TX_BD_CONTROL                       0x04CC
+#define RX_BD_CONTROL                       0x04D0
+#define UPPER_RX_Q_BASE_ADDR                0x04D4
+#define WD_COUNTER                          0x04EC
+#define AXI_TX_FULL_THRESH0                 0x04F8
+#define AXI_TX_FULL_THRESH1                 0x04FC
+#define SCREENING_TYPE_1_REGISTER_0         0x0500
+#define SCREENING_TYPE_1_REGISTER_1         0x0504
+#define SCREENING_TYPE_1_REGISTER_2         0x0508
+#define SCREENING_TYPE_1_REGISTER_3         0x050C
+#define SCREENING_TYPE_1_REGISTER_4         0x0510
+#define SCREENING_TYPE_1_REGISTER_5         0x0514
+#define SCREENING_TYPE_1_REGISTER_6         0x0518
+#define SCREENING_TYPE_1_REGISTER_7         0x051C
+#define SCREENING_TYPE_1_REGISTER_8         0x0520
+#define SCREENING_TYPE_1_REGISTER_9         0x0524
+#define SCREENING_TYPE_1_REGISTER_10        0x0528
+#define SCREENING_TYPE_1_REGISTER_11        0x052C
+#define SCREENING_TYPE_1_REGISTER_12        0x0530
+#define SCREENING_TYPE_1_REGISTER_13        0x0534
+#define SCREENING_TYPE_1_REGISTER_14        0x0538
+#define SCREENING_TYPE_1_REGISTER_15        0x053C
+#define SCREENING_TYPE_2_REGISTER_0         0x0540
+#define SCREENING_TYPE_2_REGISTER_1         0x0544
+#define SCREENING_TYPE_2_REGISTER_2         0x0548
+#define SCREENING_TYPE_2_REGISTER_3         0x054C
+#define SCREENING_TYPE_2_REGISTER_4         0x0550
+#define SCREENING_TYPE_2_REGISTER_5         0x0554
+#define SCREENING_TYPE_2_REGISTER_6         0x0558
+#define SCREENING_TYPE_2_REGISTER_7         0x055C
+#define SCREENING_TYPE_2_REGISTER_8         0x0560
+#define SCREENING_TYPE_2_REGISTER_9         0x0564
+#define SCREENING_TYPE_2_REGISTER_10        0x0568
+#define SCREENING_TYPE_2_REGISTER_11        0x056C
+#define SCREENING_TYPE_2_REGISTER_12        0x0570
+#define SCREENING_TYPE_2_REGISTER_13        0x0574
+#define SCREENING_TYPE_2_REGISTER_14        0x0578
+#define SCREENING_TYPE_2_REGISTER_15        0x057C
+#define TX_SCHED_CTRL                       0x0580
+#define BW_RATE_LIMIT_Q0TO3                 0x0590
+#define BW_RATE_LIMIT_Q4TO7                 0x0594
+#define BW_RATE_LIMIT_Q8TO11                0x0598
+#define BW_RATE_LIMIT_Q12TO15               0x059C
+#define TX_Q_SEG_ALLOC_Q_LOWER              0x05A0
+#define TX_Q_SEG_ALLOC_Q_UPPER              0x05A4
+#define RECEIVE_Q8_PTR                      0x05C0
+#define RECEIVE_Q9_PTR                      0x05C4
+#define RECEIVE_Q10_PTR                     0x05C8
+#define RECEIVE_Q11_PTR                     0x05CC
+#define RECEIVE_Q12_PTR                     0x05D0
+#define RECEIVE_Q13_PTR                     0x05D4
+#define RECEIVE_Q14_PTR                     0x05D8
+#define RECEIVE_Q15_PTR                     0x05DC
+#define DMA_RXBUF_SIZE_Q8                   0x05E0
+#define DMA_RXBUF_SIZE_Q9                   0x05E4
+#define DMA_RXBUF_SIZE_Q10                  0x05E8
+#define DMA_RXBUF_SIZE_Q11                  0x05EC
+#define DMA_RXBUF_SIZE_Q12                  0x05F0
+#define DMA_RXBUF_SIZE_Q13                  0x05F4
+#define DMA_RXBUF_SIZE_Q14                  0x05F8
+#define DMA_RXBUF_SIZE_Q15                  0x05FC
+#define INT_Q1_ENABLE                       0x0600
+#define INT_Q2_ENABLE                       0x0604
+#define INT_Q3_ENABLE                       0x0608
+#define INT_Q4_ENABLE                       0x060C
+#define INT_Q5_ENABLE                       0x0610
+#define INT_Q6_ENABLE                       0x0614
+#define INT_Q7_ENABLE                       0x0618
+#define INT_Q1_DISABLE                      0x0620
+#define INT_Q2_DISABLE                      0x0624
+#define INT_Q3_DISABLE                      0x0628
+#define INT_Q4_DISABLE                      0x062C
+#define INT_Q5_DISABLE                      0x0630
+#define INT_Q6_DISABLE                      0x0634
+#define INT_Q7_DISABLE                      0x0638
+#define INT_Q1_MASK                         0x0640
+#define INT_Q2_MASK                         0x0644
+#define INT_Q3_MASK                         0x0648
+#define INT_Q4_MASK                         0x064C
+#define INT_Q5_MASK                         0x0650
+#define INT_Q6_MASK                         0x0654
+#define INT_Q7_MASK                         0x0658
+#define INT_Q8_ENABLE                       0x0660
+#define INT_Q9_ENABLE                       0x0664
+#define INT_Q10_ENABLE                      0x0668
+#define INT_Q11_ENABLE                      0x066C
+#define INT_Q12_ENABLE                      0x0670
+#define INT_Q13_ENABLE                      0x0674
+#define INT_Q14_ENABLE                      0x0678
+#define INT_Q15_ENABLE                      0x067C
+#define INT_Q8_DISABLE                      0x0680
+#define INT_Q9_DISABLE                      0x0684
+#define INT_Q10_DISABLE                     0x0688
+#define INT_Q11_DISABLE                     0x068C
+#define INT_Q12_DISABLE                     0x0690
+#define INT_Q13_DISABLE                     0x0694
+#define INT_Q14_DISABLE                     0x0698
+#define INT_Q15_DISABLE                     0x069C
+#define INT_Q8_MASK                         0x06A0
+#define INT_Q9_MASK                         0x06A4
+#define INT_Q10_MASK                        0x06A8
+#define INT_Q11_MASK                        0x06AC
+#define INT_Q12_MASK                        0x06B0
+#define INT_Q13_MASK                        0x06B4
+#define INT_Q14_MASK                        0x06B8
+#define INT_Q15_MASK                        0x06BC
+#define SCREENING_TYPE_2_ETHERTYPE_REG_0    0x06E0
+#define SCREENING_TYPE_2_ETHERTYPE_REG_1    0x06E4
+#define SCREENING_TYPE_2_ETHERTYPE_REG_2    0x06E8
+#define SCREENING_TYPE_2_ETHERTYPE_REG_3    0x06EC
+#define SCREENING_TYPE_2_ETHERTYPE_REG_4    0x06F0
+#define SCREENING_TYPE_2_ETHERTYPE_REG_5    0x06F4
+#define SCREENING_TYPE_2_ETHERTYPE_REG_6    0x06F8
+#define SCREENING_TYPE_2_ETHERTYPE_REG_7    0x06FC
+#define TYPE2_COMPARE_0_WORD_0              0x0700
+#define TYPE2_COMPARE_0_WORD_1              0x0704
+#define TYPE2_COMPARE_1_WORD_0              0x0708
+#define TYPE2_COMPARE_1_WORD_1              0x070C
+#define TYPE2_COMPARE_2_WORD_0              0x0710
+#define TYPE2_COMPARE_2_WORD_1              0x0714
+#define TYPE2_COMPARE_3_WORD_0              0x0718
+#define TYPE2_COMPARE_3_WORD_1              0x071C
+#define TYPE2_COMPARE_4_WORD_0              0x0720
+#define TYPE2_COMPARE_4_WORD_1              0x0724
+#define TYPE2_COMPARE_5_WORD_0              0x0728
+#define TYPE2_COMPARE_5_WORD_1              0x072C
+#define TYPE2_COMPARE_6_WORD_0              0x0730
+#define TYPE2_COMPARE_6_WORD_1              0x0734
+#define TYPE2_COMPARE_7_WORD_0              0x0738
+#define TYPE2_COMPARE_7_WORD_1              0x073C
+#define TYPE2_COMPARE_8_WORD_0              0x0740
+#define TYPE2_COMPARE_8_WORD_1              0x0744
+#define TYPE2_COMPARE_9_WORD_0              0x0748
+#define TYPE2_COMPARE_9_WORD_1              0x074C
+#define TYPE2_COMPARE_10_WORD_0             0x0750
+#define TYPE2_COMPARE_10_WORD_1             0x0754
+#define TYPE2_COMPARE_11_WORD_0             0x0758
+#define TYPE2_COMPARE_11_WORD_1             0x075C
+#define TYPE2_COMPARE_12_WORD_0             0x0760
+#define TYPE2_COMPARE_12_WORD_1             0x0764
+#define TYPE2_COMPARE_13_WORD_0             0x0768
+#define TYPE2_COMPARE_13_WORD_1             0x076C
+#define TYPE2_COMPARE_14_WORD_0             0x0770
+#define TYPE2_COMPARE_14_WORD_1             0x0774
+#define TYPE2_COMPARE_15_WORD_0             0x0778
+#define TYPE2_COMPARE_15_WORD_1             0x077C
+#define TYPE2_COMPARE_16_WORD_0             0x0780
+#define TYPE2_COMPARE_16_WORD_1             0x0784
+#define TYPE2_COMPARE_17_WORD_0             0x0788
+#define TYPE2_COMPARE_17_WORD_1             0x078C
+#define TYPE2_COMPARE_18_WORD_0             0x0790
+#define TYPE2_COMPARE_18_WORD_1             0x0794
+#define TYPE2_COMPARE_19_WORD_0             0x0798
+#define TYPE2_COMPARE_19_WORD_1             0x079C
+#define TYPE2_COMPARE_20_WORD_0             0x07A0
+#define TYPE2_COMPARE_20_WORD_1             0x07A4
+#define TYPE2_COMPARE_21_WORD_0             0x07A8
+#define TYPE2_COMPARE_21_WORD_1             0x07AC
+#define TYPE2_COMPARE_22_WORD_0             0x07B0
+#define TYPE2_COMPARE_22_WORD_1             0x07B4
+#define TYPE2_COMPARE_23_WORD_0             0x07B8
+#define TYPE2_COMPARE_23_WORD_1             0x07BC
+#define TYPE2_COMPARE_24_WORD_0             0x07C0
+#define TYPE2_COMPARE_24_WORD_1             0x07C4
+#define TYPE2_COMPARE_25_WORD_0             0x07C8
+#define TYPE2_COMPARE_25_WORD_1             0x07CC
+#define TYPE2_COMPARE_26_WORD_0             0x07D0
+#define TYPE2_COMPARE_26_WORD_1             0x07D4
+#define TYPE2_COMPARE_27_WORD_0             0x07D8
+#define TYPE2_COMPARE_27_WORD_1             0x07DC
+#define TYPE2_COMPARE_28_WORD_0             0x07E0
+#define TYPE2_COMPARE_28_WORD_1             0x07E4
+#define TYPE2_COMPARE_29_WORD_0             0x07E8
+#define TYPE2_COMPARE_29_WORD_1             0x07EC
+#define TYPE2_COMPARE_30_WORD_0             0x07F0
+#define TYPE2_COMPARE_30_WORD_1             0x07F4
+#define TYPE2_COMPARE_31_WORD_0             0x07F8
+#define TYPE2_COMPARE_31_WORD_1             0x07FC
+#define ENST_START_TIME_Q8                  0x0800
+#define ENST_START_TIME_Q9                  0x0804
+#define ENST_START_TIME_Q10                 0x0808
+#define ENST_START_TIME_Q11                 0x080C
+#define ENST_START_TIME_Q12                 0x0810
+#define ENST_START_TIME_Q13                 0x0814
+#define ENST_START_TIME_Q14                 0x0818
+#define ENST_START_TIME_Q15                 0x081C
+#define ENST_ON_TIME_Q8                     0x0820
+#define ENST_ON_TIME_Q9                     0x0824
+#define ENST_ON_TIME_Q10                    0x0828
+#define ENST_ON_TIME_Q11                    0x082C
+#define ENST_ON_TIME_Q12                    0x0830
+#define ENST_ON_TIME_Q13                    0x0834
+#define ENST_ON_TIME_Q14                    0x0838
+#define ENST_ON_TIME_Q15                    0x083C
+#define ENST_OFF_TIME_Q8                    0x0840
+#define ENST_OFF_TIME_Q9                    0x0844
+#define ENST_OFF_TIME_Q10                   0x0848
+#define ENST_OFF_TIME_Q11                   0x084C
+#define ENST_OFF_TIME_Q12                   0x0850
+#define ENST_OFF_TIME_Q13                   0x0854
+#define ENST_OFF_TIME_Q14                   0x0858
+#define ENST_OFF_TIME_Q15                   0x085C
+#define ENST_CONTROL                        0x0880
+#define RX_Q0_FLUSH                         0x0B00
+#define RX_Q1_FLUSH                         0x0B04
+#define RX_Q2_FLUSH                         0x0B08
+#define RX_Q3_FLUSH                         0x0B0C
+#define RX_Q4_FLUSH                         0x0B10
+#define RX_Q5_FLUSH                         0x0B14
+#define RX_Q6_FLUSH                         0x0B18
+#define RX_Q7_FLUSH                         0x0B1C
+#define RX_Q8_FLUSH                         0x0B20
+#define RX_Q9_FLUSH                         0x0B24
+#define RX_Q10_FLUSH                        0x0B28
+#define RX_Q11_FLUSH                        0x0B2C
+#define RX_Q12_FLUSH                        0x0B30
+#define RX_Q13_FLUSH                        0x0B34
+#define RX_Q14_FLUSH                        0x0B38
+#define RX_Q15_FLUSH                        0x0B3C
+#define SCR2_REG0_RATE_LIMIT                0x0B40
+#define SCR2_REG1_RATE_LIMIT                0x0B44
+#define SCR2_REG2_RATE_LIMIT                0x0B48
+#define SCR2_REG3_RATE_LIMIT                0x0B4C
+#define SCR2_REG4_RATE_LIMIT                0x0B50
+#define SCR2_REG5_RATE_LIMIT                0x0B54
+#define SCR2_REG6_RATE_LIMIT                0x0B58
+#define SCR2_REG7_RATE_LIMIT                0x0B5C
+#define SCR2_REG8_RATE_LIMIT                0x0B60
+#define SCR2_REG9_RATE_LIMIT                0x0B64
+#define SCR2_REG10_RATE_LIMIT               0x0B68
+#define SCR2_REG11_RATE_LIMIT               0x0B6C
+#define SCR2_REG12_RATE_LIMIT               0x0B70
+#define SCR2_REG13_RATE_LIMIT               0x0B74
+#define SCR2_REG14_RATE_LIMIT               0x0B78
+#define SCR2_REG15_RATE_LIMIT               0x0B7C
+#define SCR2_RATE_STATUS                    0x0B80
+#define ASF_INT_STATUS                      0x0E00
+#define ASF_INT_RAW_STATUS                  0x0E04
+#define ASF_INT_MASK                        0x0E08
+#define ASF_INT_TEST                        0x0E0C
+#define ASF_FATAL_NONFATAL_SELECT           0x0E10
+#define ASF_TRANS_TO_FAULT_MASK             0x0E34
+#define ASF_TRANS_TO_FAULT_STATUS           0x0E38
+#define ASF_PROTOCOL_FAULT_MASK             0x0E40
+#define ASF_PROTOCOL_FAULT_STATUS           0x0E44
+
+/* Register bit field definitions *******************************************/
+
+/* NETWORK_CONTROL:
+ * The network control register contains general MAC control functions
+ * for both receiver and transmitter.
+ */
+
+#define NETWORK_CONTROL_LOOPBACK                                (1 << 0)
+#define NETWORK_CONTROL_LOOPBACK_LOCAL                          (1 << 1)
+#define NETWORK_CONTROL_ENABLE_RECEIVE                          (1 << 2)
+#define NETWORK_CONTROL_ENABLE_TRANSMIT                         (1 << 3)
+#define NETWORK_CONTROL_MAN_PORT_EN                             (1 << 4)
+#define NETWORK_CONTROL_CLEAR_ALL_STATS_REGS                    (1 << 5)
+#define NETWORK_CONTROL_INC_ALL_STATS_REGS                      (1 << 6)
+#define NETWORK_CONTROL_STATS_WRITE_EN                          (1 << 7)
+#define NETWORK_CONTROL_BACK_PRESSURE                           (1 << 8)
+#define NETWORK_CONTROL_TRANSMIT_START                          (1 << 9)
+#define NETWORK_CONTROL_TRANSMIT_HALT                           (1 << 10)
+#define NETWORK_CONTROL_TX_PAUSE_FRAME_REQ                      (1 << 11)
+#define NETWORK_CONTROL_TX_PAUSE_FRAME_ZERO                     (1 << 12)
+#define NETWORK_CONTROL_STORE_RX_TS                             (1 << 15)
+#define NETWORK_CONTROL_PFC_ENABLE                              (1 << 16)
+#define NETWORK_CONTROL_TRANSMIT_PFC_PRIORITY_BASED_PAUSE_FRAME (1 << 17)
+#define NETWORK_CONTROL_FLUSH_RX_PKT_PCLK                       (1 << 18)
+#define NETWORK_CONTROL_TX_LPI_EN                               (1 << 19)
+#define NETWORK_CONTROL_PTP_UNICAST_ENA                         (1 << 20)
+#define NETWORK_CONTROL_ALT_SGMII_MODE                          (1 << 21)
+#define NETWORK_CONTROL_STORE_UDP_OFFSET                        (1 << 22)
+#define NETWORK_CONTROL_EXT_TSU_PORT_ENABLE                     (1 << 23)
+#define NETWORK_CONTROL_ONE_STEP_SYNC_MODE                      (1 << 24)
+#define NETWORK_CONTROL_PFC_CTRL                                (1 << 25)
+#define NETWORK_CONTROL_EXT_RXQ_SEL_EN                          (1 << 26)
+#define NETWORK_CONTROL_OSS_CORRECTION_FIELD                    (1 << 27)
+#define NETWORK_CONTROL_SEL_MII_ON_RGMII                        (1 << 28)
+#define NETWORK_CONTROL_TWO_PT_FIVE_GIG                         (1 << 29)
+#define NETWORK_CONTROL_IFG_EATS_QAV_CREDIT                     (1 << 30)
+
+/* NETWORK_CONFIG:
+ * The network configuration register contains functions for
+ * setting the mode of operation for the Gigabit Ethernet MAC.
+ */
+
+#define NETWORK_CONFIG_SPEED                                    (1 << 0)
+#define NETWORK_CONFIG_FULL_DUPLEX                              (1 << 1)
+#define NETWORK_CONFIG_DISCARD_NON_VLAN_FRAMES                  (1 << 2)
+#define NETWORK_CONFIG_JUMBO_FRAMES                             (1 << 3)
+#define NETWORK_CONFIG_COPY_ALL_FRAMES                          (1 << 4)
+#define NETWORK_CONFIG_NO_BROADCAST                             (1 << 5)
+#define NETWORK_CONFIG_MULTICAST_HASH_ENABLE                    (1 << 6)
+#define NETWORK_CONFIG_UNICAST_HASH_ENABLE                      (1 << 7)
+#define NETWORK_CONFIG_RECEIVE_1536_BYTE_FRAMES                 (1 << 8)
+#define NETWORK_CONFIG_EXTERNAL_ADDRESS_MATCH_ENABLE            (1 << 9)
+#define NETWORK_CONFIG_GIGABIT_MODE_ENABLE                      (1 << 10)
+#define NETWORK_CONFIG_PCS_SELECT                               (1 << 11)
+#define NETWORK_CONFIG_RETRY_TEST                               (1 << 12)
+#define NETWORK_CONFIG_PAUSE_ENABLE                             (1 << 13)
+#define NETWORK_CONFIG_RECEIVE_BUFFER_OFFSET_MASK               (3)
+#define NETWORK_CONFIG_RECEIVE_BUFFER_OFFSET_SHIFT              (14)
+#define NETWORK_CONFIG_LENGTH_FIELD_ERROR_FRAME_DISCARD         (1 << 16)
+#define NETWORK_CONFIG_FCS_REMOVE                               (1 << 17)
+#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT                  (18)
+#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK                   (7)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8                    (0 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16                   (1 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32                   (2 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48                   (3 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64                   (4 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96                   (5 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT                     (21)
+#define NETWORK_CONFIG_DATA_BUS_WIDTH_MASK                      (3)
+#define NETWORK_CONFIG_DISABLE_COPY_OF_PAUSE_FRAMES             (1 << 23)
+#define NETWORK_CONFIG_RECEIVE_CHECKSUM_OFFLOAD_ENABLE          (1 << 24)
+#define NETWORK_CONFIG_EN_HALF_DUPLEX_RX                        (1 << 25)
+#define NETWORK_CONFIG_IGNORE_RX_FCS                            (1 << 26)
+#define NETWORK_CONFIG_SGMII_MODE_ENABLE                        (1 << 27)
+#define NETWORK_CONFIG_IPG_STRETCH_ENABLE                       (1 << 28)
+#define NETWORK_CONFIG_NSP_CHANGE                               (1 << 29)
+#define NETWORK_CONFIG_IGNORE_IPG_RX_ER                         (1 << 30)
+#define NETWORK_CONFIG_UNI_DIRECTION_ENABLE                     (1 << 31)
+
+/* NETWORK_STATUS:
+ * The network status register returns status information with respect
+ * to the PHY management MDIO interface, the PCS, priority flow control,
+ * LPI and other status.
+ */
+
+#define NETWORK_STATUS_PCS_LINK_STATE                           (1 << 0)
+#define NETWORK_STATUS_MDIO_IN                                  (1 << 1)
+#define NETWORK_STATUS_MAN_DONE                                 (1 << 2)
+#define NETWORK_STATUS_MAC_FULL_DUPLEX                          (1 << 3)
+#define NETWORK_STATUS_MAC_PAUSE_RX_EN                          (1 << 4)
+#define NETWORK_STATUS_MAC_PAUSE_TX_EN                          (1 << 5)
+#define NETWORK_STATUS_PFC_NEGOTIATE_PCLK                       (1 << 6)
+#define NETWORK_STATUS_LPI_INDICATE_PCLK                        (1 << 7)
+#define NETWORK_STATUS_AXI_XACTION_OUTSTANDING                  (1 << 8)
+#define NETWORK_STATUS_LINK_FAULT_INDICATION_SHIFT              (1 << 9)
+#define NETWORK_STATUS_LINK_FAULT_INDICATION_MASK               (7)
+
+/* DMA_CONFIG:
+ * DMA Configuration Register
+ */
+
+#define DMA_CONFIG_AMBA_BURST_LENGTH_SHIFT          (0)       /* Bits: 0-4 */
+#define DMA_CONFIG_AMBA_BURST_LENGTH_MASK           (0x1f)
+#define DMA_CONFIG_HDR_DATA_SPLITTING_EN            (1 << 5)  /* Bit 5 */
+#define DMA_CONFIG_ENDIAN_SWAP_MANAGEMENT           (1 << 6)  /* Bit 6 */
+#define DMA_CONFIG_ENDIAN_SWAP_PACKET               (1 << 7)  /* Bit 7 */
+#define DMA_CONFIG_RX_PBUF_SIZE_SHIFT               (8)       /* Bits: 8-9 */
+#define DMA_CONFIG_RX_PBUF_SIZE_MASK                (3)
+#define DMA_CONFIG_TX_PBUF_SIZE                     (1 << 10) /* Bit 10 */
+#define DMA_CONFIG_TX_PBUF_TCP_EN                   (1 << 11) /* Bit 11 */
+#define DMA_CONFIG_INFINITE_LAST_DBUF_SIZE_EN       (1 << 12) /* Bit 12 */
+#define DMA_CONFIG_CRC_ERROR_REPORT                 (1 << 13) /* Bit 13 */
+#define DMA_CONFIG_RX_BUF_SIZE_MASK                 (0xff)
+#define DMA_CONFIG_RX_BUF_SIZE_SHIFT                (16)
+#define DMA_CONFIG_FORCE_DISCARD_ON_ERR             (1 << 24) /* Bit 24 */
+#define DMA_CONFIG_FORCE_MAX_AMBA_BURST_RX          (1 << 25) /* Bit 25 */
+#define DMA_CONFIG_FORCE_MAX_AMBA_BURST_TX          (1 << 26) /* Bit 26 */
+                                                              /* Bit 27 reserved */
+#define DMA_CONFIG_RX_BD_EXTENDED_MODE_EN           (1 << 28) /* Bit 28 */
+#define DMA_CONFIG_TX_BD_EXTENDED_MODE_EN           (1 << 29) /* Bit 29 */
+#define DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1             (1 << 30) /* Bit 30 */
+                                                              /* Bit 31 reserved */
+
+/* TRANSMIT_STATUS:
+ * This register, when read, provides details of the status
+ * of the transmit path. Once read, individual bits may be cleared
+ * by writing 1 to them. It is not possible to set a bit to 1 by writing
+ * to the register.
+ */
+
+#define TRANSMIT_STATUS_USED_BIT_READ               (1 << 0)
+#define TRANSMIT_STATUS_COLLISION_OCCURED           (1 << 1)
+#define TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED        (1 << 2)
+#define TRANSMIT_STATUS_TRANSMIT_GO                 (1 << 3)
+#define TRANSMIT_STATUS_AMBA_ERROR                  (1 << 4)
+#define TRANSMIT_STATUS_TRANSMIT_COMPLETE           (1 << 5)
+#define TRANSMIT_STATUS_UNDERRUN                    (1 << 6)
+#define TRANSMIT_STATUS_LATE_COLLISION              (1 << 7)
+#define TRANSMIT_STATUS_RESP_NOT_OK                 (1 << 8)
+#define TRANSMIT_STATUS_TX_MAC_LOCKUP               (1 << 9)
+#define TRANSMIT_STATUS_TX_DMA_LOCKUP               (1 << 10)
+
+/* RECEIVE_STATUS:
+ * This register, when read, provides details of the status
+ * of the receive path. Once read, individual bits may be cleared
+ * by writing 1 to them. It is not possible to set a bit to 1 by writing
+ * to the register.
+ */
+#define RECEIVE_STATUS_BUFFER_NOT_AVAILABLE         (1 << 0)
+#define RECEIVE_STATUS_FRAME_RECEIVED               (1 << 1)
+#define RECEIVE_STATUS_RECEIVE_OVERRUN              (1 << 2)
+#define RECEIVE_STATUS_RESP_NOT_OK                  (1 << 3)
+#define RECEIVE_STATUS_RX_MAC_LOCKUP                (1 << 4)
+#define RECEIVE_STATUS_RX_DMA_LOCKUP                (1 << 5)
+
+/* PHY_MANAGEMENT:
+ */
+
+#define PHY_MANAGEMENT_PHY_DATA_SHIFT               (0)
+#define PHY_MANAGEMENT_PHY_DATA_MASK                (0xffff)
+#define PHY_MANAGEMENT_PHY_DATA(n)                  ((n & PHY_MANAGEMENT_PHY_DATA_MASK) << PHY_MANAGEMENT_PHY_DATA_SHIFT)
+#define PHY_MANAGEMENT_WRITE10                      (2 << PHY_MANAGEMENT_WRITE10_SHIFT)
+#define PHY_MANAGEMENT_WRITE10_SHIFT                (16)
+#define PHY_MANAGEMENT_WRITE10_MASK                 (3)
+#define PHY_MANAGEMENT_REG_ADDRESS_SHIFT            (18)
+#define PHY_MANAGEMENT_REG_ADDRESS_MASK             (0x1f)
+#define PHY_MANAGEMENT_REG_ADDRESS(n)               ((n & PHY_MANAGEMENT_REG_ADDRESS_MASK ) << PHY_MANAGEMENT_REG_ADDRESS_SHIFT)
+#define PHY_MANAGEMENT_PHY_ADDRESS_SHIFT            (23)
+#define PHY_MANAGEMENT_PHY_ADDRESS_MASK             (0x1f)
+#define PHY_MANAGEMENT_PHY_ADDRESS(n)               ((n & PHY_MANAGEMENT_PHY_ADDRESS_MASK ) << PHY_MANAGEMENT_PHY_ADDRESS_SHIFT)
+#define PHY_MANAGEMENT_OPERATION_SHIFT              (28)
+#define PHY_MANAGEMENT_OPERATION_MASK               (3)
+#  define PHY_MANAGEMENT_OPERATION_READ             (2 << PHY_MANAGEMENT_OPERATION_SHIFT)
+#  define PHY_MANAGEMENT_OPERATION_WRITE            (1 << PHY_MANAGEMENT_OPERATION_SHIFT)
+#define PHY_MANAGEMENT_WRITE_1                      (1 << 30)
+#define PHY_MANAGEMENT_WRITE_0                      (1 << 31)
+
+/* irq status, enable and disable */
+
+#define GEM_INT_MANAGEMENT_DONE                     (1 << 0)
+#define GEM_INT_RECEIVE_COMPLETE                    (1 << 1)
+#define GEM_INT_RECEIVE_BUFFER_USED_BIT_READ        (1 << 2)
+#define GEM_INT_TRANSMIT_BUFFER_USED_BIT_READ       (1 << 3)
+#define GEM_INT_TRANSMIT_BUFFER_UNDERRUN            (1 << 4)
+#define GEM_INT_RETRY_LIMIT_EXCEEDED                (1 << 5)
+#define GEM_INT_AMBA_ERROR                          (1 << 6)
+#define GEM_INT_TRANSMIT_COMPLETE                   (1 << 7)
+#define GEM_INT_RECEIVE_OVERRUN                     (1 << 10)
+#define GEM_INT_RESP_NOT_OK                         (1 << 11)
+
+/* Receive buffer descriptor:  Address word */
+
+#define GEM_RX_DMA_ADDR_OWNER       (1 << 0)     /* Bit 0:  1=Software owns; 0=GMAC owns */
+#define GEM_RX_DMA_ADDR_WRAP        (1 << 1)     /* Bit 1:  Last descriptor in list */
+#define GEM_RX_DMA_ADDR_MASK        (0xfffffffc) /* Bits 2-31: Aligned buffer address */
+
+/* Receive buffer descriptor:  status word */
+
+#define GEM_RX_DMA_STATUS_FRAME_LEN_SHIFT  (0)         /* Bits 0-12: Frame length */
+#define GEM_RX_DMA_STATUS_FRAME_LEN_MASK   (0x00000fff)
+#define GEM_RX_DMA_STATUS_SOF              (1 << 14)   /* Bit 14: Start of frame */
+#define GEM_RX_DMA_STATUS_EOF              (1 << 15)   /* Bit 14: End of frame */
+
+#define GEM_TX_DMA_LAST                    (1 << 15)  /* the last buffer in a frame */
+#define GEM_TX_DMA_NO_CRC                  (1 << 16)  /* don't calc and append crc */
+                                                      /* 17 - 19 reserved */
+#define GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT    (20)       /* */
+#define GEM_TX_DMA_OFFLOAD_ERRORS_MASK     (7)        /* */

Review comment:
       done.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829070796



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       Yes.
   This is preferred way to disable call to riscv_netinitialize() on early boot.
   if CONFIG_NETDEV_LATEINIT is defined then riscv_netinitialize() is defined as empty.
   exactly same as on any arm targets.
   I will add the mpfs_ethernet.h file for prototypes.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829870337



##########
File path: Documentation/platforms/risc-v/mpfs/index.rst
##########
@@ -64,17 +64,18 @@ The following list indicates the state of peripherals' support in NuttX:
 ============   =======  =====
 Peripheral     Support  NOTES
 ============   =======  =====
-GPIO           Yes      
+GPIO           Yes
 MMUART         Yes      Uart mode only
-SPI            Yes      
-I2C            Yes      
+SPI            Yes
+I2C            Yes
 eMMC SD/SDIO   Yes      No PHY training
+USB            Yes
+Ethernet MAC   Yes
 Timers         No
-Watchdog       No       
-RTC            No       
-CAN            No       
-eNVM           No       
-USB            No

Review comment:
       https://github.com/apache/incubator-nuttx/pull/5740/files/d90ca07a70fbac0ef05afdacc25b724d1ef26157#diff-cfc2aa7229960754dad201acf4a97c917b8ddf85c5ceba9494c10a1bddb24fe9R72
   




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r828964221



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))

Review comment:
       Why not just `>> 32`?

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;

Review comment:
       Minor
   ```suggestion
     if (ndx >= 32)
       {
         regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
         regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
       }
     else
       {
         regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
         regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
       }
   
     regval  = mac_getreg(priv, regaddr1);
     regval &= ~(1 << (ndx % 32));
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;

Review comment:
       Minor
   ```suggestion
     regoffset = (ndx >= 32) ? HASH_TOP : HASH_BOTTOM;
   
     regval = mac_getreg(priv, regoffset);
     regval |= 1 << (ndx % 32);
   ```

##########
File path: boards/risc-v/bl602/bl602evb/configs/wifi/defconfig
##########
@@ -73,6 +73,7 @@ CONFIG_MTD=y
 CONFIG_MTD_PARTITION=y
 CONFIG_NET=y
 CONFIG_NETDB_DNSCLIENT=y
+CONFIG_NETDEV_LATEINIT=y

Review comment:
       Why do we need this?

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */

Review comment:
       2 spaces

##########
File path: boards/risc-v/esp32c3/esp32c3-devkit/configs/ble/defconfig
##########
@@ -29,6 +29,7 @@ CONFIG_IDLETHREAD_STACKSIZE=3072
 CONFIG_INIT_ENTRYPOINT="nsh_main"
 CONFIG_INTELHEX_BINARY=y
 CONFIG_NAME_MAX=48
+CONFIG_NETDEV_LATEINIT=y

Review comment:
       Why do we need this?

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);

Review comment:
       ```suggestion
         *priv->queue[qi].dma_rxbuf_size = MPFS_MAC_RX_BUF_VALUE;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       Please explain why we need this? I do not see that `mpfs_ethinitialize` is called out of this file. I mean either it should be `static` always or we should add `mpfs_ethinitialize(0);` calls to `mpfs_bringup.c` and function prototype should be exposed in the header, isn't it?

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);

Review comment:
       ```suggestion
         *priv->queue[qi].int_disable = 0xffffffff;
         *priv->queue[qi].int_status  = 0xffffffff;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;

Review comment:
       ```suggestion
     uint32_t net_config = 0;
     uint32_t net_control = 0;
     uint32_t dma_config = 0;
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#issuecomment-1069630319


   @pkarashchenko  @acassis  How about those config changes? to get all risc-v targets build.
   Do you you want that i make those config changes? and should it be on this PR or do you want it separated?
   Or maybe somebody else will do the config changes and after that this could be merged.
   What you think?
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko merged pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
pkarashchenko merged pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740


   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jlaitine commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jlaitine commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r826966650



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3855 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;

Review comment:
       ```suggestion
     uint32_t *addr = (uint32_t *)(priv->regbase + offset);
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r825869657



##########
File path: arch/risc-v/src/mpfs/Kconfig
##########
@@ -317,10 +337,151 @@ config MPFS_DMA
 	---help---
 		Enable DMA Support. MPFS DMA is Memory-to-Memory only.
 
-menu "MPFS Others"
+menu "Ethernet MAC configuration"
+	depends on MPFS_ETHMAC
+
+choice
+	prompt "MII Interface mode"
+
+config MPFS_MAC_SGMII
+	bool "SGMII"
+	---help---
+		Use Ethernet SGMII interface.
+
+config MPFS_MAC_GMII
+	bool "GMII"
+	---help---
+		Use Ethernet GMII interface.
+endchoice
+
+config MPFS_PHYADDR
+	int "PHY address"
+	default 1
+	---help---
+		The 5-bit address of the PHY on the board.  Default: 1
+
+config MPFS_MAC_NO_BROADCAST
+	bool "Disable Broadcast"
+	default n
+	---help---
+		Select to disable receipt of broadcast packets.
+
+config MPFS_MAC_AUTONEG
+	bool "Use autonegotiation"
+	default y
+	---help---
+		Use PHY autonegotiation to determine speed and mode
+
+config MPFS_MAC_DISABLE_1000MBPS
+	bool "Disable Gigabit mode"
+	default n
+	depends on MPFS_MAC_AUTONEG
+	---help---
+		Select to disable Gigabit speed support.
+		If disabled then autonegotiation don't advertise 1GB mode
+
+config MPFS_PHYINIT
+	bool "Use board phyinit"
+	default n
+	---help---
+		call mpfs_phy_boardinitialize() on init
+
+if !MPFS_MAC_AUTONEG
+
+config MPFS_MAC_ETHFD
+	bool "Full duplex"
+	default n
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to
+		select full duplex mode. Default: half-duplex
+
+choice
+	prompt "MAC Speed"
+	default MPFS_MAC_ETH100MBPS
+	---help---
+		If autonegotiation is not used, then you must select the fixed speed
+		of the PHY
+
+config MPFS_MAC_ETH10MBPS
+	bool "10 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 10 MBps
+		speed.  Default: 100 Mbps
+
+config MPFS_MAC_ETH100MBPS
+	bool "100 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 100 MBps
+		speed.  Default: 100 Mbps
+
+config MPFS_MAC_ETH1000MBPS
+	bool "1000 Mbps"
+	---help---

Review comment:
       depends on !MPFS_MAC_DISABLE_1000MBPS




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r828121831



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == (void *)MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == (void *)MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)(1UL)) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS (values from baremetal driver) */
+
+  mac_putreg(priv, PCS_CONTROL, 0x8000);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config =  (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                 DMA_CONFIG_TX_PBUF_SIZE |
+                 (((uint32_t)(0x3ul)) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                 ((uint32_t)(0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr;
+  r &= ~1;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif
+{
+  struct mpfs_ethmac_s *priv;
+  int ret = OK;
+  uintptr_t base;
+
+  ninfo("intf: %d\n", intf);
+
+  /* Get the interface structure associated with this interface number. */
+
+  DEBUGASSERT(intf < MPFS_NETHERNET);
+  priv = &g_mpfsethmac[intf];
+
+  /* Initialize the driver structure */
+
+  memset(priv, 0, sizeof(struct mpfs_ethmac_s));
+  priv->dev.d_buf     = g_pktbuf;       /* Single packet buffer */
+  priv->dev.d_ifup    = mpfs_ifup;      /* I/F up (new IP address) callback */
+  priv->dev.d_ifdown  = mpfs_ifdown;    /* I/F down callback */
+  priv->dev.d_txavail = mpfs_txavail;   /* New TX data callback */
+#ifdef CONFIG_NET_MCASTGROUP
+  priv->dev.d_addmac  = mpfs_addmac;    /* Add multicast MAC address */
+  priv->dev.d_rmmac   = mpfs_rmmac;     /* Remove multicast MAC address */
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+  priv->dev.d_ioctl   = mpfs_ioctl;     /* Support PHY ioctl() calls */
+#endif
+  priv->dev.d_private = priv;           /* Used to recover private state */
+  priv->intf          = intf;           /* Remember the interface number */
+  priv->regbase       = g_regbases[intf];
+  priv->mac_q_int[0]  = g_irq_numbers[intf][0];
+  priv->mac_q_int[1]  = g_irq_numbers[intf][1];
+  priv->mac_q_int[2]  = g_irq_numbers[intf][2];
+  priv->mac_q_int[3]  = g_irq_numbers[intf][3];
+  ninfo("mac @ 0x%" PRIx64 "\n", priv->regbase);
+
+  base = priv->regbase;
+  priv->queue[0].int_status     = (uint32_t *)(base + INT_STATUS);
+  priv->queue[1].int_status     = (uint32_t *)(base + INT_Q1_STATUS);
+  priv->queue[2].int_status     = (uint32_t *)(base + INT_Q2_STATUS);
+  priv->queue[3].int_status     = (uint32_t *)(base + INT_Q3_STATUS);
+  priv->queue[0].int_mask       = (uint32_t *)(base + INT_MASK);
+  priv->queue[1].int_mask       = (uint32_t *)(base + INT_Q1_MASK);
+  priv->queue[2].int_mask       = (uint32_t *)(base + INT_Q2_MASK);
+  priv->queue[3].int_mask       = (uint32_t *)(base + INT_Q3_MASK);
+  priv->queue[0].int_enable     = (uint32_t *)(base + INT_ENABLE);
+  priv->queue[1].int_enable     = (uint32_t *)(base + INT_Q1_ENABLE);
+  priv->queue[2].int_enable     = (uint32_t *)(base + INT_Q2_ENABLE);
+  priv->queue[3].int_enable     = (uint32_t *)(base + INT_Q3_ENABLE);
+  priv->queue[0].int_disable    = (uint32_t *)(base + INT_DISABLE);
+  priv->queue[1].int_disable    = (uint32_t *)(base + INT_Q1_DISABLE);
+  priv->queue[2].int_disable    = (uint32_t *)(base + INT_Q2_DISABLE);
+  priv->queue[3].int_disable    = (uint32_t *)(base + INT_Q3_DISABLE);
+  priv->queue[0].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q_PTR);
+  priv->queue[1].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q1_PTR);
+  priv->queue[2].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q2_PTR);
+  priv->queue[3].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q3_PTR);
+  priv->queue[0].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q_PTR);
+  priv->queue[1].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q1_PTR);
+  priv->queue[2].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q2_PTR);
+  priv->queue[3].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q3_PTR);
+  priv->queue[0].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q1);
+  priv->queue[1].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q1);
+  priv->queue[2].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q2);
+  priv->queue[3].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q3);
+
+  /* MPU hack for ETH DMA if not enabled by bootloader */
+
+#ifdef CONFIG_MPFS_MPU_DMA_ENABLE
+#  ifdef CONFIG_MPFS_ETHMAC_0
+  putreg64(0x1f00000fffffffff, MPFS_PMPCFG_ETH0_0);
+#  endif
+#  ifdef CONFIG_MPFS_ETHMAC_1
+  putreg64(0x1f00000fffffffff, MPFS_PMPCFG_ETH1_0);
+#  endif
+#endif
+
+  /* Allocate buffers */
+
+  ret = mpfs_buffer_initialize(priv, 0);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_buffer_initialize failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Attach the IRQ to the driver */
+
+  if (irq_attach(priv->mac_q_int[0], mpfs_interrupt_0, priv))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  if (irq_attach(priv->mac_q_int[1], mpfs_interrupt_1, NULL))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  if (irq_attach(priv->mac_q_int[2], mpfs_interrupt_2, NULL))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  if (irq_attach(priv->mac_q_int[3], mpfs_interrupt_3, NULL))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  /* Enable clocking to the GMAC peripheral (just for mpfs_ifdown()) */
+
+  /* MAC HW clock enable and reset */
+
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET, 0,
+                  SYSREG_SUBBLK_CLOCK_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+
+      for (volatile int i = 0; i < 10000; i++)
+        {
+          ;
+        }

Review comment:
       I'll remove this. it was attempt to get reset working on some weird cases. but has no effect actually.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r825896731



##########
File path: arch/risc-v/src/mpfs/Kconfig
##########
@@ -317,10 +337,151 @@ config MPFS_DMA
 	---help---
 		Enable DMA Support. MPFS DMA is Memory-to-Memory only.
 
-menu "MPFS Others"
+menu "Ethernet MAC configuration"
+	depends on MPFS_ETHMAC
+
+choice
+	prompt "MII Interface mode"
+
+config MPFS_MAC_SGMII
+	bool "SGMII"
+	---help---
+		Use Ethernet SGMII interface.
+
+config MPFS_MAC_GMII
+	bool "GMII"
+	---help---
+		Use Ethernet GMII interface.
+endchoice
+
+config MPFS_PHYADDR
+	int "PHY address"
+	default 1
+	---help---
+		The 5-bit address of the PHY on the board.  Default: 1
+
+config MPFS_MAC_NO_BROADCAST
+	bool "Disable Broadcast"
+	default n
+	---help---
+		Select to disable receipt of broadcast packets.
+
+config MPFS_MAC_AUTONEG
+	bool "Use autonegotiation"
+	default y
+	---help---
+		Use PHY autonegotiation to determine speed and mode
+
+config MPFS_MAC_DISABLE_1000MBPS
+	bool "Disable Gigabit mode"
+	default n
+	depends on MPFS_MAC_AUTONEG
+	---help---
+		Select to disable Gigabit speed support.
+		If disabled then autonegotiation don't advertise 1GB mode
+
+config MPFS_PHYINIT
+	bool "Use board phyinit"
+	default n
+	---help---
+		call mpfs_phy_boardinitialize() on init
+
+if !MPFS_MAC_AUTONEG
+
+config MPFS_MAC_ETHFD
+	bool "Full duplex"
+	default n
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to
+		select full duplex mode. Default: half-duplex
+
+choice
+	prompt "MAC Speed"
+	default MPFS_MAC_ETH100MBPS
+	---help---
+		If autonegotiation is not used, then you must select the fixed speed
+		of the PHY
+
+config MPFS_MAC_ETH10MBPS
+	bool "10 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 10 MBps
+		speed.  Default: 100 Mbps
+
+config MPFS_MAC_ETH100MBPS
+	bool "100 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 100 MBps
+		speed.  Default: 100 Mbps
+
+config MPFS_MAC_ETH1000MBPS
+	bool "1000 Mbps"
+	---help---

Review comment:
       not really. the MPFS_MAC_DISABLE_1000MBPS is for auto negotiate only. maybe confusing name.
   should it be like MPFS_MAC_AUTONEG_DISABLE_1000MBPS?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r828413319



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)

Review comment:
       ```suggestion
     if (priv->queue[queue].txbuffer == NULL)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)

Review comment:
       ```suggestion
     if (priv->queue[queue].rx_desc_tab == NULL)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)

Review comment:
       ```suggestion
     if (priv->queue[queue].rxbuffer == NULL)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)

Review comment:
       ```suggestion
     if (priv->queue[queue].tx_desc_tab == NULL)
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#issuecomment-1066741554


   all recommended fixes applied. thanks...
   checkpatch failed me with tabs :-/ 
   I was using different editor than usually and missed the tabs.
   Should the checkpatch/nxstyle detect the TAB char: 🤔 


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jlaitine commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jlaitine commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r826967057



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3855 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;

Review comment:
       ```suggestion
     uint32_t *addr = (uint32_t *)(priv->regbase + offset);
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829086104



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       Lets go with `int mpfs_ethinitialize(int intf);` only and remove `static` case




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829818549



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3837 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */

Review comment:
       2 spaces alignment

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3837 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t              *rxbuffer;        /* Allocated RX buffers */
+        uint8_t              *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t             txhead;           /* Circular buffer head index */
+        uint32_t             txtail;           /* Circular buffer tail index */
+        uint32_t             rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t    *int_status;      /* interrupt status */
+        volatile uint32_t    *int_mask;        /* interrupt mask */
+        volatile uint32_t    *int_enable;      /* interrupt enable */
+        volatile uint32_t    *int_disable;     /* interrupt disable */
+        volatile uint32_t    *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t    *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t    *dma_rxbuf_size;  /* RX queue buffer size */

Review comment:
       2 spaces alignment




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829869162



##########
File path: Documentation/platforms/risc-v/mpfs/index.rst
##########
@@ -64,17 +64,18 @@ The following list indicates the state of peripherals' support in NuttX:
 ============   =======  =====
 Peripheral     Support  NOTES
 ============   =======  =====
-GPIO           Yes      
+GPIO           Yes
 MMUART         Yes      Uart mode only
-SPI            Yes      
-I2C            Yes      
+SPI            Yes
+I2C            Yes
 eMMC SD/SDIO   Yes      No PHY training
+USB            Yes
+Ethernet MAC   Yes
 Timers         No
-Watchdog       No       
-RTC            No       
-CAN            No       
-eNVM           No       
-USB            No

Review comment:
       USB should work?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r826315267



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;

Review comment:
       ```suggestion
     UNUSED(irq);
     UNUSED(context);
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;

Review comment:
       Please use `UNUSED` macro

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;

Review comment:
       Please use `UNUSED` macro

##########
File path: arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h
##########
@@ -0,0 +1,701 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H
+#define __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/mpfs_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets *********************************************************/
+
+#define NETWORK_CONTROL                     0x0000
+#define NETWORK_CONFIG                      0x0004
+#define NETWORK_STATUS                      0x0008
+#define DMA_CONFIG                          0x0010
+#define TRANSMIT_STATUS                     0x0014
+#define RECEIVE_Q_PTR                       0x0018
+#define TRANSMIT_Q_PTR                      0x001C
+#define RECEIVE_STATUS                      0x0020
+#define INT_STATUS                          0x0024
+#define INT_ENABLE                          0x0028
+#define INT_DISABLE                         0x002C
+#define INT_MASK                            0x0030
+#define PHY_MANAGEMENT                      0x0034
+#define PAUSE_TIME                          0x0038
+#define TX_PAUSE_QUANTUM                    0x003C
+#define PBUF_TXCUTTHRU                      0x0040
+#define PBUF_RXCUTTHRU                      0x0044
+#define JUMBO_MAX_LENGTH                    0x0048
+#define AXI_MAX_PIPELINE                    0x0054
+#define RSC_CONTROL                         0x0058
+#define INT_MODERATION                      0x005C
+#define SYS_WAKE_TIME                       0x0060
+#define LOCKUP_CONFIG                       0x0068
+#define MAC_LOCKUP_TIME                     0x006C
+#define LOCKUP_CONFIG3                      0x0070
+#define RX_WATER_MARK                       0x007C
+#define HASH_BOTTOM                         0x0080
+#define HASH_TOP                            0x0084
+#define SPEC_ADD1_BOTTOM                    0x0088
+#define SPEC_ADD1_TOP                       0x008C
+#define SPEC_ADD2_BOTTOM                    0x0090
+#define SPEC_ADD2_TOP                       0x0094
+#define SPEC_ADD3_BOTTOM                    0x0098
+#define SPEC_ADD3_TOP                       0x009C
+#define SPEC_ADD4_BOTTOM                    0x00A0
+#define SPEC_ADD4_TOP                       0x00A4
+#define SPEC_TYPE1                          0x00A8
+#define SPEC_TYPE2                          0x00AC
+#define SPEC_TYPE3                          0x00B0
+#define SPEC_TYPE4                          0x00B4
+#define WOL_REGISTER                        0x00B8
+#define STRETCH_RATIO                       0x00BC
+#define STACKED_VLAN                        0x00C0
+#define TX_PFC_PAUSE                        0x00C4
+#define MASK_ADD1_BOTTOM                    0x00C8
+#define MASK_ADD1_TOP                       0x00CC
+#define DMA_ADDR_OR_MASK                    0x00D0
+#define RX_PTP_UNICAST                      0x00D4
+#define TX_PTP_UNICAST                      0x00D8
+#define TSU_NSEC_CMP                        0x00DC
+#define TSU_SEC_CMP                         0x00E0
+#define TSU_MSB_SEC_CMP                     0x00E4
+#define TSU_PTP_TX_MSB_SEC                  0x00E8
+#define TSU_PTP_RX_MSB_SEC                  0x00EC
+#define TSU_PEER_TX_MSB_SEC                 0x00F0
+#define TSU_PEER_RX_MSB_SEC                 0x00F4
+#define DPRAM_FILL_DBG                      0x00F8
+#define REVISION_REG                        0x00FC
+#define OCTETS_TXED_BOTTOM                  0x0100
+#define OCTETS_TXED_TOP                     0x0104
+#define FRAMES_TXED_OK                      0x0108
+#define BROADCAST_TXED                      0x010C
+#define MULTICAST_TXED                      0x0110
+#define PAUSE_FRAMES_TXED                   0x0114
+#define FRAMES_TXED_64                      0x0118
+#define FRAMES_TXED_65                      0x011C
+#define FRAMES_TXED_128                     0x0120
+#define FRAMES_TXED_256                     0x0124
+#define FRAMES_TXED_512                     0x0128
+#define FRAMES_TXED_1024                    0x012C
+#define FRAMES_TXED_1519                    0x0130
+#define TX_UNDERRUNS                        0x0134
+#define SINGLE_COLLISIONS                   0x0138
+#define MULTIPLE_COLLISIONS                 0x013C
+#define EXCESSIVE_COLLISIONS                0x0140
+#define LATE_COLLISIONS                     0x0144
+#define DEFERRED_FRAMES                     0x0148
+#define CRS_ERRORS                          0x014C
+#define OCTETS_RXED_BOTTOM                  0x0150
+#define OCTETS_RXED_TOP                     0x0154
+#define FRAMES_RXED_OK                      0x0158
+#define BROADCAST_RXED                      0x015C
+#define MULTICAST_RXED                      0x0160
+#define PAUSE_FRAMES_RXED                   0x0164
+#define FRAMES_RXED_64                      0x0168
+#define FRAMES_RXED_65                      0x016C
+#define FRAMES_RXED_128                     0x0170
+#define FRAMES_RXED_256                     0x0174
+#define FRAMES_RXED_512                     0x0178
+#define FRAMES_RXED_1024                    0x017C
+#define FRAMES_RXED_1519                    0x0180
+#define UNDERSIZE_FRAMES                    0x0184
+#define EXCESSIVE_RX_LENGTH                 0x0188
+#define RX_JABBERS                          0x018C
+#define FCS_ERRORS                          0x0190
+#define RX_LENGTH_ERRORS                    0x0194
+#define RX_SYMBOL_ERRORS                    0x0198
+#define ALIGNMENT_ERRORS                    0x019C
+#define RX_RESOURCE_ERRORS                  0x01A0
+#define RX_OVERRUNS                         0x01A4
+#define RX_IP_CK_ERRORS                     0x01A8
+#define RX_TCP_CK_ERRORS                    0x01AC
+#define RX_UDP_CK_ERRORS                    0x01B0
+#define AUTO_FLUSHED_PKTS                   0x01B4
+#define TSU_TIMER_INCR_SUB_NSEC             0x01BC
+#define TSU_TIMER_MSB_SEC                   0x01C0
+#define TSU_STROBE_MSB_SEC                  0x01C4
+#define TSU_STROBE_SEC                      0x01C8
+#define TSU_STROBE_NSEC                     0x01CC
+#define TSU_TIMER_SEC                       0x01D0
+#define TSU_TIMER_NSEC                      0x01D4
+#define TSU_TIMER_ADJUST                    0x01D8
+#define TSU_TIMER_INCR                      0x01DC
+#define TSU_PTP_TX_SEC                      0x01E0
+#define TSU_PTP_TX_NSEC                     0x01E4
+#define TSU_PTP_RX_SEC                      0x01E8
+#define TSU_PTP_RX_NSEC                     0x01EC
+#define TSU_PEER_TX_SEC                     0x01F0
+#define TSU_PEER_TX_NSEC                    0x01F4
+#define TSU_PEER_RX_SEC                     0x01F8
+#define TSU_PEER_RX_NSEC                    0x01FC
+#define PCS_CONTROL                         0x0200
+#define PFC_STATUS                          0x026C
+#define RX_LPI                              0x0270
+#define RX_LPI_TIME                         0x0274
+#define TX_LPI                              0x0278
+#define TX_LPI_TIME                         0x027C
+#define DESIGNCFG_DEBUG1                    0x0280
+#define DESIGNCFG_DEBUG2                    0x0284
+#define DESIGNCFG_DEBUG3                    0x0288
+#define DESIGNCFG_DEBUG4                    0x028C
+#define DESIGNCFG_DEBUG5                    0x0290
+#define DESIGNCFG_DEBUG6                    0x0294
+#define DESIGNCFG_DEBUG7                    0x0298
+#define DESIGNCFG_DEBUG8                    0x029C
+#define DESIGNCFG_DEBUG9                    0x02A0
+#define DESIGNCFG_DEBUG10                   0x02A4
+#define DESIGNCFG_DEBUG11                   0x02A8
+#define DESIGNCFG_DEBUG12                   0x02AC
+#define AXI_QOS_CFG_0                       0x02E0
+#define AXI_QOS_CFG_1                       0x02E4
+#define AXI_QOS_CFG_2                       0x02E8
+#define AXI_QOS_CFG_3                       0x02EC
+#define INT_Q1_STATUS                       0x0400
+#define INT_Q2_STATUS                       0x0404
+#define INT_Q3_STATUS                       0x0408
+#define INT_Q4_STATUS                       0x040C
+#define INT_Q5_STATUS                       0x0410
+#define INT_Q6_STATUS                       0x0414
+#define INT_Q7_STATUS                       0x0418
+#define INT_Q8_STATUS                       0x041C
+#define INT_Q9_STATUS                       0x0420
+#define INT_Q10_STATUS                      0x0424
+#define INT_Q11_STATUS                      0x0428
+#define INT_Q12_STATUS                      0x042C
+#define INT_Q13_STATUS                      0x0430
+#define INT_Q14_STATUS                      0x0434
+#define INT_Q15_STATUS                      0x0438
+#define TRANSMIT_Q1_PTR                     0x0440
+#define TRANSMIT_Q2_PTR                     0x0444
+#define TRANSMIT_Q3_PTR                     0x0448
+#define TRANSMIT_Q4_PTR                     0x044C
+#define TRANSMIT_Q5_PTR                     0x0450
+#define TRANSMIT_Q6_PTR                     0x0454
+#define TRANSMIT_Q7_PTR                     0x0458
+#define TRANSMIT_Q8_PTR                     0x045C
+#define TRANSMIT_Q9_PTR                     0x0460
+#define TRANSMIT_Q10_PTR                    0x0464
+#define TRANSMIT_Q11_PTR                    0x0468
+#define TRANSMIT_Q12_PTR                    0x046C
+#define TRANSMIT_Q13_PTR                    0x0470
+#define TRANSMIT_Q14_PTR                    0x0474
+#define TRANSMIT_Q15_PTR                    0x0478
+#define RECEIVE_Q1_PTR                      0x0480
+#define RECEIVE_Q2_PTR                      0x0484
+#define RECEIVE_Q3_PTR                      0x0488
+#define RECEIVE_Q4_PTR                      0x048C
+#define RECEIVE_Q5_PTR                      0x0490
+#define RECEIVE_Q6_PTR                      0x0494
+#define RECEIVE_Q7_PTR                      0x0498
+#define DMA_RXBUF_SIZE_Q1                   0x04A0
+#define DMA_RXBUF_SIZE_Q2                   0x04A4
+#define DMA_RXBUF_SIZE_Q3                   0x04A8
+#define DMA_RXBUF_SIZE_Q4                   0x04AC
+#define DMA_RXBUF_SIZE_Q5                   0x04B0
+#define DMA_RXBUF_SIZE_Q6                   0x04B4
+#define DMA_RXBUF_SIZE_Q7                   0x04B8
+#define CBS_CONTROL                         0x04BC
+#define CBS_IDLESLOPE_Q_A                   0x04C0
+#define CBS_IDLESLOPE_Q_B                   0x04C4
+#define UPPER_TX_Q_BASE_ADDR                0x04C8
+#define TX_BD_CONTROL                       0x04CC
+#define RX_BD_CONTROL                       0x04D0
+#define UPPER_RX_Q_BASE_ADDR                0x04D4
+#define WD_COUNTER                          0x04EC
+#define AXI_TX_FULL_THRESH0                 0x04F8
+#define AXI_TX_FULL_THRESH1                 0x04FC
+#define SCREENING_TYPE_1_REGISTER_0         0x0500
+#define SCREENING_TYPE_1_REGISTER_1         0x0504
+#define SCREENING_TYPE_1_REGISTER_2         0x0508
+#define SCREENING_TYPE_1_REGISTER_3         0x050C
+#define SCREENING_TYPE_1_REGISTER_4         0x0510
+#define SCREENING_TYPE_1_REGISTER_5         0x0514
+#define SCREENING_TYPE_1_REGISTER_6         0x0518
+#define SCREENING_TYPE_1_REGISTER_7         0x051C
+#define SCREENING_TYPE_1_REGISTER_8         0x0520
+#define SCREENING_TYPE_1_REGISTER_9         0x0524
+#define SCREENING_TYPE_1_REGISTER_10        0x0528
+#define SCREENING_TYPE_1_REGISTER_11        0x052C
+#define SCREENING_TYPE_1_REGISTER_12        0x0530
+#define SCREENING_TYPE_1_REGISTER_13        0x0534
+#define SCREENING_TYPE_1_REGISTER_14        0x0538
+#define SCREENING_TYPE_1_REGISTER_15        0x053C
+#define SCREENING_TYPE_2_REGISTER_0         0x0540
+#define SCREENING_TYPE_2_REGISTER_1         0x0544
+#define SCREENING_TYPE_2_REGISTER_2         0x0548
+#define SCREENING_TYPE_2_REGISTER_3         0x054C
+#define SCREENING_TYPE_2_REGISTER_4         0x0550
+#define SCREENING_TYPE_2_REGISTER_5         0x0554
+#define SCREENING_TYPE_2_REGISTER_6         0x0558
+#define SCREENING_TYPE_2_REGISTER_7         0x055C
+#define SCREENING_TYPE_2_REGISTER_8         0x0560
+#define SCREENING_TYPE_2_REGISTER_9         0x0564
+#define SCREENING_TYPE_2_REGISTER_10        0x0568
+#define SCREENING_TYPE_2_REGISTER_11        0x056C
+#define SCREENING_TYPE_2_REGISTER_12        0x0570
+#define SCREENING_TYPE_2_REGISTER_13        0x0574
+#define SCREENING_TYPE_2_REGISTER_14        0x0578
+#define SCREENING_TYPE_2_REGISTER_15        0x057C
+#define TX_SCHED_CTRL                       0x0580
+#define BW_RATE_LIMIT_Q0TO3                 0x0590
+#define BW_RATE_LIMIT_Q4TO7                 0x0594
+#define BW_RATE_LIMIT_Q8TO11                0x0598
+#define BW_RATE_LIMIT_Q12TO15               0x059C
+#define TX_Q_SEG_ALLOC_Q_LOWER              0x05A0
+#define TX_Q_SEG_ALLOC_Q_UPPER              0x05A4
+#define RECEIVE_Q8_PTR                      0x05C0
+#define RECEIVE_Q9_PTR                      0x05C4
+#define RECEIVE_Q10_PTR                     0x05C8
+#define RECEIVE_Q11_PTR                     0x05CC
+#define RECEIVE_Q12_PTR                     0x05D0
+#define RECEIVE_Q13_PTR                     0x05D4
+#define RECEIVE_Q14_PTR                     0x05D8
+#define RECEIVE_Q15_PTR                     0x05DC
+#define DMA_RXBUF_SIZE_Q8                   0x05E0
+#define DMA_RXBUF_SIZE_Q9                   0x05E4
+#define DMA_RXBUF_SIZE_Q10                  0x05E8
+#define DMA_RXBUF_SIZE_Q11                  0x05EC
+#define DMA_RXBUF_SIZE_Q12                  0x05F0
+#define DMA_RXBUF_SIZE_Q13                  0x05F4
+#define DMA_RXBUF_SIZE_Q14                  0x05F8
+#define DMA_RXBUF_SIZE_Q15                  0x05FC
+#define INT_Q1_ENABLE                       0x0600
+#define INT_Q2_ENABLE                       0x0604
+#define INT_Q3_ENABLE                       0x0608
+#define INT_Q4_ENABLE                       0x060C
+#define INT_Q5_ENABLE                       0x0610
+#define INT_Q6_ENABLE                       0x0614
+#define INT_Q7_ENABLE                       0x0618
+#define INT_Q1_DISABLE                      0x0620
+#define INT_Q2_DISABLE                      0x0624
+#define INT_Q3_DISABLE                      0x0628
+#define INT_Q4_DISABLE                      0x062C
+#define INT_Q5_DISABLE                      0x0630
+#define INT_Q6_DISABLE                      0x0634
+#define INT_Q7_DISABLE                      0x0638
+#define INT_Q1_MASK                         0x0640
+#define INT_Q2_MASK                         0x0644
+#define INT_Q3_MASK                         0x0648
+#define INT_Q4_MASK                         0x064C
+#define INT_Q5_MASK                         0x0650
+#define INT_Q6_MASK                         0x0654
+#define INT_Q7_MASK                         0x0658
+#define INT_Q8_ENABLE                       0x0660
+#define INT_Q9_ENABLE                       0x0664
+#define INT_Q10_ENABLE                      0x0668
+#define INT_Q11_ENABLE                      0x066C
+#define INT_Q12_ENABLE                      0x0670
+#define INT_Q13_ENABLE                      0x0674
+#define INT_Q14_ENABLE                      0x0678
+#define INT_Q15_ENABLE                      0x067C
+#define INT_Q8_DISABLE                      0x0680
+#define INT_Q9_DISABLE                      0x0684
+#define INT_Q10_DISABLE                     0x0688
+#define INT_Q11_DISABLE                     0x068C
+#define INT_Q12_DISABLE                     0x0690
+#define INT_Q13_DISABLE                     0x0694
+#define INT_Q14_DISABLE                     0x0698
+#define INT_Q15_DISABLE                     0x069C
+#define INT_Q8_MASK                         0x06A0
+#define INT_Q9_MASK                         0x06A4
+#define INT_Q10_MASK                        0x06A8
+#define INT_Q11_MASK                        0x06AC
+#define INT_Q12_MASK                        0x06B0
+#define INT_Q13_MASK                        0x06B4
+#define INT_Q14_MASK                        0x06B8
+#define INT_Q15_MASK                        0x06BC
+#define SCREENING_TYPE_2_ETHERTYPE_REG_0    0x06E0
+#define SCREENING_TYPE_2_ETHERTYPE_REG_1    0x06E4
+#define SCREENING_TYPE_2_ETHERTYPE_REG_2    0x06E8
+#define SCREENING_TYPE_2_ETHERTYPE_REG_3    0x06EC
+#define SCREENING_TYPE_2_ETHERTYPE_REG_4    0x06F0
+#define SCREENING_TYPE_2_ETHERTYPE_REG_5    0x06F4
+#define SCREENING_TYPE_2_ETHERTYPE_REG_6    0x06F8
+#define SCREENING_TYPE_2_ETHERTYPE_REG_7    0x06FC
+#define TYPE2_COMPARE_0_WORD_0              0x0700
+#define TYPE2_COMPARE_0_WORD_1              0x0704
+#define TYPE2_COMPARE_1_WORD_0              0x0708
+#define TYPE2_COMPARE_1_WORD_1              0x070C
+#define TYPE2_COMPARE_2_WORD_0              0x0710
+#define TYPE2_COMPARE_2_WORD_1              0x0714
+#define TYPE2_COMPARE_3_WORD_0              0x0718
+#define TYPE2_COMPARE_3_WORD_1              0x071C
+#define TYPE2_COMPARE_4_WORD_0              0x0720
+#define TYPE2_COMPARE_4_WORD_1              0x0724
+#define TYPE2_COMPARE_5_WORD_0              0x0728
+#define TYPE2_COMPARE_5_WORD_1              0x072C
+#define TYPE2_COMPARE_6_WORD_0              0x0730
+#define TYPE2_COMPARE_6_WORD_1              0x0734
+#define TYPE2_COMPARE_7_WORD_0              0x0738
+#define TYPE2_COMPARE_7_WORD_1              0x073C
+#define TYPE2_COMPARE_8_WORD_0              0x0740
+#define TYPE2_COMPARE_8_WORD_1              0x0744
+#define TYPE2_COMPARE_9_WORD_0              0x0748
+#define TYPE2_COMPARE_9_WORD_1              0x074C
+#define TYPE2_COMPARE_10_WORD_0             0x0750
+#define TYPE2_COMPARE_10_WORD_1             0x0754
+#define TYPE2_COMPARE_11_WORD_0             0x0758
+#define TYPE2_COMPARE_11_WORD_1             0x075C
+#define TYPE2_COMPARE_12_WORD_0             0x0760
+#define TYPE2_COMPARE_12_WORD_1             0x0764
+#define TYPE2_COMPARE_13_WORD_0             0x0768
+#define TYPE2_COMPARE_13_WORD_1             0x076C
+#define TYPE2_COMPARE_14_WORD_0             0x0770
+#define TYPE2_COMPARE_14_WORD_1             0x0774
+#define TYPE2_COMPARE_15_WORD_0             0x0778
+#define TYPE2_COMPARE_15_WORD_1             0x077C
+#define TYPE2_COMPARE_16_WORD_0             0x0780
+#define TYPE2_COMPARE_16_WORD_1             0x0784
+#define TYPE2_COMPARE_17_WORD_0             0x0788
+#define TYPE2_COMPARE_17_WORD_1             0x078C
+#define TYPE2_COMPARE_18_WORD_0             0x0790
+#define TYPE2_COMPARE_18_WORD_1             0x0794
+#define TYPE2_COMPARE_19_WORD_0             0x0798
+#define TYPE2_COMPARE_19_WORD_1             0x079C
+#define TYPE2_COMPARE_20_WORD_0             0x07A0
+#define TYPE2_COMPARE_20_WORD_1             0x07A4
+#define TYPE2_COMPARE_21_WORD_0             0x07A8
+#define TYPE2_COMPARE_21_WORD_1             0x07AC
+#define TYPE2_COMPARE_22_WORD_0             0x07B0
+#define TYPE2_COMPARE_22_WORD_1             0x07B4
+#define TYPE2_COMPARE_23_WORD_0             0x07B8
+#define TYPE2_COMPARE_23_WORD_1             0x07BC
+#define TYPE2_COMPARE_24_WORD_0             0x07C0
+#define TYPE2_COMPARE_24_WORD_1             0x07C4
+#define TYPE2_COMPARE_25_WORD_0             0x07C8
+#define TYPE2_COMPARE_25_WORD_1             0x07CC
+#define TYPE2_COMPARE_26_WORD_0             0x07D0
+#define TYPE2_COMPARE_26_WORD_1             0x07D4
+#define TYPE2_COMPARE_27_WORD_0             0x07D8
+#define TYPE2_COMPARE_27_WORD_1             0x07DC
+#define TYPE2_COMPARE_28_WORD_0             0x07E0
+#define TYPE2_COMPARE_28_WORD_1             0x07E4
+#define TYPE2_COMPARE_29_WORD_0             0x07E8
+#define TYPE2_COMPARE_29_WORD_1             0x07EC
+#define TYPE2_COMPARE_30_WORD_0             0x07F0
+#define TYPE2_COMPARE_30_WORD_1             0x07F4
+#define TYPE2_COMPARE_31_WORD_0             0x07F8
+#define TYPE2_COMPARE_31_WORD_1             0x07FC
+#define ENST_START_TIME_Q8                  0x0800
+#define ENST_START_TIME_Q9                  0x0804
+#define ENST_START_TIME_Q10                 0x0808
+#define ENST_START_TIME_Q11                 0x080C
+#define ENST_START_TIME_Q12                 0x0810
+#define ENST_START_TIME_Q13                 0x0814
+#define ENST_START_TIME_Q14                 0x0818
+#define ENST_START_TIME_Q15                 0x081C
+#define ENST_ON_TIME_Q8                     0x0820
+#define ENST_ON_TIME_Q9                     0x0824
+#define ENST_ON_TIME_Q10                    0x0828
+#define ENST_ON_TIME_Q11                    0x082C
+#define ENST_ON_TIME_Q12                    0x0830
+#define ENST_ON_TIME_Q13                    0x0834
+#define ENST_ON_TIME_Q14                    0x0838
+#define ENST_ON_TIME_Q15                    0x083C
+#define ENST_OFF_TIME_Q8                    0x0840
+#define ENST_OFF_TIME_Q9                    0x0844
+#define ENST_OFF_TIME_Q10                   0x0848
+#define ENST_OFF_TIME_Q11                   0x084C
+#define ENST_OFF_TIME_Q12                   0x0850
+#define ENST_OFF_TIME_Q13                   0x0854
+#define ENST_OFF_TIME_Q14                   0x0858
+#define ENST_OFF_TIME_Q15                   0x085C
+#define ENST_CONTROL                        0x0880
+#define RX_Q0_FLUSH                         0x0B00
+#define RX_Q1_FLUSH                         0x0B04
+#define RX_Q2_FLUSH                         0x0B08
+#define RX_Q3_FLUSH                         0x0B0C
+#define RX_Q4_FLUSH                         0x0B10
+#define RX_Q5_FLUSH                         0x0B14
+#define RX_Q6_FLUSH                         0x0B18
+#define RX_Q7_FLUSH                         0x0B1C
+#define RX_Q8_FLUSH                         0x0B20
+#define RX_Q9_FLUSH                         0x0B24
+#define RX_Q10_FLUSH                        0x0B28
+#define RX_Q11_FLUSH                        0x0B2C
+#define RX_Q12_FLUSH                        0x0B30
+#define RX_Q13_FLUSH                        0x0B34
+#define RX_Q14_FLUSH                        0x0B38
+#define RX_Q15_FLUSH                        0x0B3C
+#define SCR2_REG0_RATE_LIMIT                0x0B40
+#define SCR2_REG1_RATE_LIMIT                0x0B44
+#define SCR2_REG2_RATE_LIMIT                0x0B48
+#define SCR2_REG3_RATE_LIMIT                0x0B4C
+#define SCR2_REG4_RATE_LIMIT                0x0B50
+#define SCR2_REG5_RATE_LIMIT                0x0B54
+#define SCR2_REG6_RATE_LIMIT                0x0B58
+#define SCR2_REG7_RATE_LIMIT                0x0B5C
+#define SCR2_REG8_RATE_LIMIT                0x0B60
+#define SCR2_REG9_RATE_LIMIT                0x0B64
+#define SCR2_REG10_RATE_LIMIT               0x0B68
+#define SCR2_REG11_RATE_LIMIT               0x0B6C
+#define SCR2_REG12_RATE_LIMIT               0x0B70
+#define SCR2_REG13_RATE_LIMIT               0x0B74
+#define SCR2_REG14_RATE_LIMIT               0x0B78
+#define SCR2_REG15_RATE_LIMIT               0x0B7C
+#define SCR2_RATE_STATUS                    0x0B80
+#define ASF_INT_STATUS                      0x0E00
+#define ASF_INT_RAW_STATUS                  0x0E04
+#define ASF_INT_MASK                        0x0E08
+#define ASF_INT_TEST                        0x0E0C
+#define ASF_FATAL_NONFATAL_SELECT           0x0E10
+#define ASF_TRANS_TO_FAULT_MASK             0x0E34
+#define ASF_TRANS_TO_FAULT_STATUS           0x0E38
+#define ASF_PROTOCOL_FAULT_MASK             0x0E40
+#define ASF_PROTOCOL_FAULT_STATUS           0x0E44
+
+/* Register bit field definitions *******************************************/
+
+/* NETWORK_CONTROL:
+ * The network control register contains general MAC control functions
+ * for both receiver and transmitter.
+ */
+
+#define NETWORK_CONTROL_LOOPBACK                                (1 << 0)
+#define NETWORK_CONTROL_LOOPBACK_LOCAL                          (1 << 1)
+#define NETWORK_CONTROL_ENABLE_RECEIVE                          (1 << 2)
+#define NETWORK_CONTROL_ENABLE_TRANSMIT                         (1 << 3)
+#define NETWORK_CONTROL_MAN_PORT_EN                             (1 << 4)
+#define NETWORK_CONTROL_CLEAR_ALL_STATS_REGS                    (1 << 5)
+#define NETWORK_CONTROL_INC_ALL_STATS_REGS                      (1 << 6)
+#define NETWORK_CONTROL_STATS_WRITE_EN                          (1 << 7)
+#define NETWORK_CONTROL_BACK_PRESSURE                           (1 << 8)
+#define NETWORK_CONTROL_TRANSMIT_START                          (1 << 9)
+#define NETWORK_CONTROL_TRANSMIT_HALT                           (1 << 10)
+#define NETWORK_CONTROL_TX_PAUSE_FRAME_REQ                      (1 << 11)
+#define NETWORK_CONTROL_TX_PAUSE_FRAME_ZERO                     (1 << 12)
+#define NETWORK_CONTROL_STORE_RX_TS                             (1 << 15)
+#define NETWORK_CONTROL_PFC_ENABLE                              (1 << 16)
+#define NETWORK_CONTROL_TRANSMIT_PFC_PRIORITY_BASED_PAUSE_FRAME (1 << 17)
+#define NETWORK_CONTROL_FLUSH_RX_PKT_PCLK                       (1 << 18)
+#define NETWORK_CONTROL_TX_LPI_EN                               (1 << 19)
+#define NETWORK_CONTROL_PTP_UNICAST_ENA                         (1 << 20)
+#define NETWORK_CONTROL_ALT_SGMII_MODE                          (1 << 21)
+#define NETWORK_CONTROL_STORE_UDP_OFFSET                        (1 << 22)
+#define NETWORK_CONTROL_EXT_TSU_PORT_ENABLE                     (1 << 23)
+#define NETWORK_CONTROL_ONE_STEP_SYNC_MODE                      (1 << 24)
+#define NETWORK_CONTROL_PFC_CTRL                                (1 << 25)
+#define NETWORK_CONTROL_EXT_RXQ_SEL_EN                          (1 << 26)
+#define NETWORK_CONTROL_OSS_CORRECTION_FIELD                    (1 << 27)
+#define NETWORK_CONTROL_SEL_MII_ON_RGMII                        (1 << 28)
+#define NETWORK_CONTROL_TWO_PT_FIVE_GIG                         (1 << 29)
+#define NETWORK_CONTROL_IFG_EATS_QAV_CREDIT                     (1 << 30)
+
+/* NETWORK_CONFIG:
+ * The network configuration register contains functions for
+ * setting the mode of operation for the Gigabit Ethernet MAC.
+ */
+
+#define NETWORK_CONFIG_SPEED                                    (1 << 0)
+#define NETWORK_CONFIG_FULL_DUPLEX                              (1 << 1)
+#define NETWORK_CONFIG_DISCARD_NON_VLAN_FRAMES                  (1 << 2)
+#define NETWORK_CONFIG_JUMBO_FRAMES                             (1 << 3)
+#define NETWORK_CONFIG_COPY_ALL_FRAMES                          (1 << 4)
+#define NETWORK_CONFIG_NO_BROADCAST                             (1 << 5)
+#define NETWORK_CONFIG_MULTICAST_HASH_ENABLE                    (1 << 6)
+#define NETWORK_CONFIG_UNICAST_HASH_ENABLE                      (1 << 7)
+#define NETWORK_CONFIG_RECEIVE_1536_BYTE_FRAMES                 (1 << 8)
+#define NETWORK_CONFIG_EXTERNAL_ADDRESS_MATCH_ENABLE            (1 << 9)
+#define NETWORK_CONFIG_GIGABIT_MODE_ENABLE                      (1 << 10)
+#define NETWORK_CONFIG_PCS_SELECT                               (1 << 11)
+#define NETWORK_CONFIG_RETRY_TEST                               (1 << 12)
+#define NETWORK_CONFIG_PAUSE_ENABLE                             (1 << 13)
+#define NETWORK_CONFIG_RECEIVE_BUFFER_OFFSET_MASK               (3)
+#define NETWORK_CONFIG_RECEIVE_BUFFER_OFFSET_SHIFT              (14)
+#define NETWORK_CONFIG_LENGTH_FIELD_ERROR_FRAME_DISCARD         (1 << 16)
+#define NETWORK_CONFIG_FCS_REMOVE                               (1 << 17)
+#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT                  (18)
+#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK                   (7)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8                    (0 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16                   (1 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32                   (2 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48                   (3 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64                   (4 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96                   (5 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT                     (21)
+#define NETWORK_CONFIG_DATA_BUS_WIDTH_MASK                      (3)
+#define NETWORK_CONFIG_DISABLE_COPY_OF_PAUSE_FRAMES             (1 << 23)
+#define NETWORK_CONFIG_RECEIVE_CHECKSUM_OFFLOAD_ENABLE          (1 << 24)
+#define NETWORK_CONFIG_EN_HALF_DUPLEX_RX                        (1 << 25)
+#define NETWORK_CONFIG_IGNORE_RX_FCS                            (1 << 26)
+#define NETWORK_CONFIG_SGMII_MODE_ENABLE                        (1 << 27)
+#define NETWORK_CONFIG_IPG_STRETCH_ENABLE                       (1 << 28)
+#define NETWORK_CONFIG_NSP_CHANGE                               (1 << 29)
+#define NETWORK_CONFIG_IGNORE_IPG_RX_ER                         (1 << 30)
+#define NETWORK_CONFIG_UNI_DIRECTION_ENABLE                     (1 << 31)
+
+/* NETWORK_STATUS:
+ * The network status register returns status information with respect
+ * to the PHY management MDIO interface, the PCS, priority flow control,
+ * LPI and other status.
+ */
+
+#define NETWORK_STATUS_PCS_LINK_STATE                           (1 << 0)
+#define NETWORK_STATUS_MDIO_IN                                  (1 << 1)
+#define NETWORK_STATUS_MAN_DONE                                 (1 << 2)
+#define NETWORK_STATUS_MAC_FULL_DUPLEX                          (1 << 3)
+#define NETWORK_STATUS_MAC_PAUSE_RX_EN                          (1 << 4)
+#define NETWORK_STATUS_MAC_PAUSE_TX_EN                          (1 << 5)
+#define NETWORK_STATUS_PFC_NEGOTIATE_PCLK                       (1 << 6)
+#define NETWORK_STATUS_LPI_INDICATE_PCLK                        (1 << 7)
+#define NETWORK_STATUS_AXI_XACTION_OUTSTANDING                  (1 << 8)
+#define NETWORK_STATUS_LINK_FAULT_INDICATION_SHIFT              (1 << 9)
+#define NETWORK_STATUS_LINK_FAULT_INDICATION_MASK               (7)
+
+/* DMA_CONFIG:
+ * DMA Configuration Register
+ */
+
+#define DMA_CONFIG_AMBA_BURST_LENGTH_SHIFT          (0)       /* Bits: 0-4 */
+#define DMA_CONFIG_AMBA_BURST_LENGTH_MASK           (0x1f)
+#define DMA_CONFIG_HDR_DATA_SPLITTING_EN            (1 << 5)  /* Bit 5 */
+#define DMA_CONFIG_ENDIAN_SWAP_MANAGEMENT           (1 << 6)  /* Bit 6 */
+#define DMA_CONFIG_ENDIAN_SWAP_PACKET               (1 << 7)  /* Bit 7 */
+#define DMA_CONFIG_RX_PBUF_SIZE_SHIFT               (8)       /* Bits: 8-9 */
+#define DMA_CONFIG_RX_PBUF_SIZE_MASK                (3)
+#define DMA_CONFIG_TX_PBUF_SIZE                     (1 << 10) /* Bit 10 */
+#define DMA_CONFIG_TX_PBUF_TCP_EN                   (1 << 11) /* Bit 11 */
+#define DMA_CONFIG_INFINITE_LAST_DBUF_SIZE_EN       (1 << 12) /* Bit 12 */
+#define DMA_CONFIG_CRC_ERROR_REPORT                 (1 << 13) /* Bit 13 */
+#define DMA_CONFIG_RX_BUF_SIZE_MASK                 (0xff)
+#define DMA_CONFIG_RX_BUF_SIZE_SHIFT                (16)
+#define DMA_CONFIG_FORCE_DISCARD_ON_ERR             (1 << 24) /* Bit 24 */
+#define DMA_CONFIG_FORCE_MAX_AMBA_BURST_RX          (1 << 25) /* Bit 25 */
+#define DMA_CONFIG_FORCE_MAX_AMBA_BURST_TX          (1 << 26) /* Bit 26 */
+                                                              /* Bit 27 reserved */
+#define DMA_CONFIG_RX_BD_EXTENDED_MODE_EN           (1 << 28) /* Bit 28 */
+#define DMA_CONFIG_TX_BD_EXTENDED_MODE_EN           (1 << 29) /* Bit 29 */
+#define DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1             (1 << 30) /* Bit 30 */
+                                                              /* Bit 31 reserved */
+
+/* TRANSMIT_STATUS:
+ * This register, when read, provides details of the status
+ * of the transmit path. Once read, individual bits may be cleared
+ * by writing 1 to them. It is not possible to set a bit to 1 by writing
+ * to the register.
+ */
+
+#define TRANSMIT_STATUS_USED_BIT_READ               (1 << 0)
+#define TRANSMIT_STATUS_COLLISION_OCCURED           (1 << 1)
+#define TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED        (1 << 2)
+#define TRANSMIT_STATUS_TRANSMIT_GO                 (1 << 3)
+#define TRANSMIT_STATUS_AMBA_ERROR                  (1 << 4)
+#define TRANSMIT_STATUS_TRANSMIT_COMPLETE           (1 << 5)
+#define TRANSMIT_STATUS_UNDERRUN                    (1 << 6)
+#define TRANSMIT_STATUS_LATE_COLLISION              (1 << 7)
+#define TRANSMIT_STATUS_RESP_NOT_OK                 (1 << 8)
+#define TRANSMIT_STATUS_TX_MAC_LOCKUP               (1 << 9)
+#define TRANSMIT_STATUS_TX_DMA_LOCKUP               (1 << 10)
+
+/* RECEIVE_STATUS:
+ * This register, when read, provides details of the status
+ * of the receive path. Once read, individual bits may be cleared
+ * by writing 1 to them. It is not possible to set a bit to 1 by writing
+ * to the register.
+ */
+#define RECEIVE_STATUS_BUFFER_NOT_AVAILABLE         (1 << 0)
+#define RECEIVE_STATUS_FRAME_RECEIVED               (1 << 1)
+#define RECEIVE_STATUS_RECEIVE_OVERRUN              (1 << 2)
+#define RECEIVE_STATUS_RESP_NOT_OK                  (1 << 3)
+#define RECEIVE_STATUS_RX_MAC_LOCKUP                (1 << 4)
+#define RECEIVE_STATUS_RX_DMA_LOCKUP                (1 << 5)
+
+/* PHY_MANAGEMENT:
+ */
+
+#define PHY_MANAGEMENT_PHY_DATA_SHIFT               (0)
+#define PHY_MANAGEMENT_PHY_DATA_MASK                (0xffff)
+#define PHY_MANAGEMENT_PHY_DATA(n)                  ((n & PHY_MANAGEMENT_PHY_DATA_MASK) << PHY_MANAGEMENT_PHY_DATA_SHIFT)
+#define PHY_MANAGEMENT_WRITE10                      (2 << PHY_MANAGEMENT_WRITE10_SHIFT)
+#define PHY_MANAGEMENT_WRITE10_SHIFT                (16)
+#define PHY_MANAGEMENT_WRITE10_MASK                 (3)
+#define PHY_MANAGEMENT_REG_ADDRESS_SHIFT            (18)
+#define PHY_MANAGEMENT_REG_ADDRESS_MASK             (0x1f)
+#define PHY_MANAGEMENT_REG_ADDRESS(n)               ((n & PHY_MANAGEMENT_REG_ADDRESS_MASK ) << PHY_MANAGEMENT_REG_ADDRESS_SHIFT)
+#define PHY_MANAGEMENT_PHY_ADDRESS_SHIFT            (23)
+#define PHY_MANAGEMENT_PHY_ADDRESS_MASK             (0x1f)
+#define PHY_MANAGEMENT_PHY_ADDRESS(n)               ((n & PHY_MANAGEMENT_PHY_ADDRESS_MASK ) << PHY_MANAGEMENT_PHY_ADDRESS_SHIFT)
+#define PHY_MANAGEMENT_OPERATION_SHIFT              (28)
+#define PHY_MANAGEMENT_OPERATION_MASK               (3)
+#  define PHY_MANAGEMENT_OPERATION_READ             (2 << PHY_MANAGEMENT_OPERATION_SHIFT)
+#  define PHY_MANAGEMENT_OPERATION_WRITE            (1 << PHY_MANAGEMENT_OPERATION_SHIFT)
+#define PHY_MANAGEMENT_WRITE_1                      (1 << 30)
+#define PHY_MANAGEMENT_WRITE_0                      (1 << 31)
+
+/* irq status, enable and disable */
+
+#define GEM_INT_MANAGEMENT_DONE                     (1 << 0)
+#define GEM_INT_RECEIVE_COMPLETE                    (1 << 1)
+#define GEM_INT_RECEIVE_BUFFER_USED_BIT_READ        (1 << 2)
+#define GEM_INT_TRANSMIT_BUFFER_USED_BIT_READ       (1 << 3)
+#define GEM_INT_TRANSMIT_BUFFER_UNDERRUN            (1 << 4)
+#define GEM_INT_RETRY_LIMIT_EXCEEDED                (1 << 5)
+#define GEM_INT_AMBA_ERROR                          (1 << 6)
+#define GEM_INT_TRANSMIT_COMPLETE                   (1 << 7)
+#define GEM_INT_RECEIVE_OVERRUN                     (1 << 10)
+#define GEM_INT_RESP_NOT_OK                         (1 << 11)
+
+/* Receive buffer descriptor:  Address word */
+
+#define GEM_RX_DMA_ADDR_OWNER       (1 << 0)     /* Bit 0:  1=Software owns; 0=GMAC owns */
+#define GEM_RX_DMA_ADDR_WRAP        (1 << 1)     /* Bit 1:  Last descriptor in list */
+#define GEM_RX_DMA_ADDR_MASK        (0xfffffffc) /* Bits 2-31: Aligned buffer address */
+
+/* Receive buffer descriptor:  status word */
+
+#define GEM_RX_DMA_STATUS_FRAME_LEN_SHIFT  (0)         /* Bits 0-12: Frame length */
+#define GEM_RX_DMA_STATUS_FRAME_LEN_MASK   (0x00000fff)
+#define GEM_RX_DMA_STATUS_SOF              (1 << 14)   /* Bit 14: Start of frame */
+#define GEM_RX_DMA_STATUS_EOF              (1 << 15)   /* Bit 14: End of frame */
+
+#define GEM_TX_DMA_LAST                    (1 << 15)  /* the last buffer in a frame */
+#define GEM_TX_DMA_NO_CRC                  (1 << 16)  /* don't calc and append crc */
+                                                      /* 17 - 19 reserved */
+#define GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT    (20)       /* */
+#define GEM_TX_DMA_OFFLOAD_ERRORS_MASK     (7)        /* */

Review comment:
       Do we need to update comments here?

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;

Review comment:
       Please use `UNUSED` macro

##########
File path: arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h
##########
@@ -0,0 +1,701 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H
+#define __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/mpfs_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets *********************************************************/
+
+#define NETWORK_CONTROL                     0x0000
+#define NETWORK_CONFIG                      0x0004
+#define NETWORK_STATUS                      0x0008
+#define DMA_CONFIG                          0x0010
+#define TRANSMIT_STATUS                     0x0014
+#define RECEIVE_Q_PTR                       0x0018
+#define TRANSMIT_Q_PTR                      0x001C
+#define RECEIVE_STATUS                      0x0020
+#define INT_STATUS                          0x0024
+#define INT_ENABLE                          0x0028
+#define INT_DISABLE                         0x002C
+#define INT_MASK                            0x0030
+#define PHY_MANAGEMENT                      0x0034
+#define PAUSE_TIME                          0x0038
+#define TX_PAUSE_QUANTUM                    0x003C
+#define PBUF_TXCUTTHRU                      0x0040
+#define PBUF_RXCUTTHRU                      0x0044
+#define JUMBO_MAX_LENGTH                    0x0048
+#define AXI_MAX_PIPELINE                    0x0054
+#define RSC_CONTROL                         0x0058
+#define INT_MODERATION                      0x005C
+#define SYS_WAKE_TIME                       0x0060
+#define LOCKUP_CONFIG                       0x0068
+#define MAC_LOCKUP_TIME                     0x006C
+#define LOCKUP_CONFIG3                      0x0070
+#define RX_WATER_MARK                       0x007C
+#define HASH_BOTTOM                         0x0080
+#define HASH_TOP                            0x0084
+#define SPEC_ADD1_BOTTOM                    0x0088
+#define SPEC_ADD1_TOP                       0x008C
+#define SPEC_ADD2_BOTTOM                    0x0090
+#define SPEC_ADD2_TOP                       0x0094
+#define SPEC_ADD3_BOTTOM                    0x0098
+#define SPEC_ADD3_TOP                       0x009C
+#define SPEC_ADD4_BOTTOM                    0x00A0
+#define SPEC_ADD4_TOP                       0x00A4
+#define SPEC_TYPE1                          0x00A8
+#define SPEC_TYPE2                          0x00AC
+#define SPEC_TYPE3                          0x00B0
+#define SPEC_TYPE4                          0x00B4
+#define WOL_REGISTER                        0x00B8
+#define STRETCH_RATIO                       0x00BC
+#define STACKED_VLAN                        0x00C0
+#define TX_PFC_PAUSE                        0x00C4
+#define MASK_ADD1_BOTTOM                    0x00C8
+#define MASK_ADD1_TOP                       0x00CC
+#define DMA_ADDR_OR_MASK                    0x00D0
+#define RX_PTP_UNICAST                      0x00D4
+#define TX_PTP_UNICAST                      0x00D8
+#define TSU_NSEC_CMP                        0x00DC
+#define TSU_SEC_CMP                         0x00E0
+#define TSU_MSB_SEC_CMP                     0x00E4
+#define TSU_PTP_TX_MSB_SEC                  0x00E8
+#define TSU_PTP_RX_MSB_SEC                  0x00EC
+#define TSU_PEER_TX_MSB_SEC                 0x00F0
+#define TSU_PEER_RX_MSB_SEC                 0x00F4
+#define DPRAM_FILL_DBG                      0x00F8
+#define REVISION_REG                        0x00FC
+#define OCTETS_TXED_BOTTOM                  0x0100
+#define OCTETS_TXED_TOP                     0x0104
+#define FRAMES_TXED_OK                      0x0108
+#define BROADCAST_TXED                      0x010C
+#define MULTICAST_TXED                      0x0110
+#define PAUSE_FRAMES_TXED                   0x0114
+#define FRAMES_TXED_64                      0x0118
+#define FRAMES_TXED_65                      0x011C
+#define FRAMES_TXED_128                     0x0120
+#define FRAMES_TXED_256                     0x0124
+#define FRAMES_TXED_512                     0x0128
+#define FRAMES_TXED_1024                    0x012C
+#define FRAMES_TXED_1519                    0x0130
+#define TX_UNDERRUNS                        0x0134
+#define SINGLE_COLLISIONS                   0x0138
+#define MULTIPLE_COLLISIONS                 0x013C
+#define EXCESSIVE_COLLISIONS                0x0140
+#define LATE_COLLISIONS                     0x0144
+#define DEFERRED_FRAMES                     0x0148
+#define CRS_ERRORS                          0x014C
+#define OCTETS_RXED_BOTTOM                  0x0150
+#define OCTETS_RXED_TOP                     0x0154
+#define FRAMES_RXED_OK                      0x0158
+#define BROADCAST_RXED                      0x015C
+#define MULTICAST_RXED                      0x0160
+#define PAUSE_FRAMES_RXED                   0x0164
+#define FRAMES_RXED_64                      0x0168
+#define FRAMES_RXED_65                      0x016C
+#define FRAMES_RXED_128                     0x0170
+#define FRAMES_RXED_256                     0x0174
+#define FRAMES_RXED_512                     0x0178
+#define FRAMES_RXED_1024                    0x017C
+#define FRAMES_RXED_1519                    0x0180
+#define UNDERSIZE_FRAMES                    0x0184
+#define EXCESSIVE_RX_LENGTH                 0x0188
+#define RX_JABBERS                          0x018C
+#define FCS_ERRORS                          0x0190
+#define RX_LENGTH_ERRORS                    0x0194
+#define RX_SYMBOL_ERRORS                    0x0198
+#define ALIGNMENT_ERRORS                    0x019C
+#define RX_RESOURCE_ERRORS                  0x01A0
+#define RX_OVERRUNS                         0x01A4
+#define RX_IP_CK_ERRORS                     0x01A8
+#define RX_TCP_CK_ERRORS                    0x01AC
+#define RX_UDP_CK_ERRORS                    0x01B0
+#define AUTO_FLUSHED_PKTS                   0x01B4
+#define TSU_TIMER_INCR_SUB_NSEC             0x01BC
+#define TSU_TIMER_MSB_SEC                   0x01C0
+#define TSU_STROBE_MSB_SEC                  0x01C4
+#define TSU_STROBE_SEC                      0x01C8
+#define TSU_STROBE_NSEC                     0x01CC
+#define TSU_TIMER_SEC                       0x01D0
+#define TSU_TIMER_NSEC                      0x01D4
+#define TSU_TIMER_ADJUST                    0x01D8
+#define TSU_TIMER_INCR                      0x01DC
+#define TSU_PTP_TX_SEC                      0x01E0
+#define TSU_PTP_TX_NSEC                     0x01E4
+#define TSU_PTP_RX_SEC                      0x01E8
+#define TSU_PTP_RX_NSEC                     0x01EC
+#define TSU_PEER_TX_SEC                     0x01F0
+#define TSU_PEER_TX_NSEC                    0x01F4
+#define TSU_PEER_RX_SEC                     0x01F8
+#define TSU_PEER_RX_NSEC                    0x01FC
+#define PCS_CONTROL                         0x0200
+#define PFC_STATUS                          0x026C
+#define RX_LPI                              0x0270
+#define RX_LPI_TIME                         0x0274
+#define TX_LPI                              0x0278
+#define TX_LPI_TIME                         0x027C
+#define DESIGNCFG_DEBUG1                    0x0280
+#define DESIGNCFG_DEBUG2                    0x0284
+#define DESIGNCFG_DEBUG3                    0x0288
+#define DESIGNCFG_DEBUG4                    0x028C
+#define DESIGNCFG_DEBUG5                    0x0290
+#define DESIGNCFG_DEBUG6                    0x0294
+#define DESIGNCFG_DEBUG7                    0x0298
+#define DESIGNCFG_DEBUG8                    0x029C
+#define DESIGNCFG_DEBUG9                    0x02A0
+#define DESIGNCFG_DEBUG10                   0x02A4
+#define DESIGNCFG_DEBUG11                   0x02A8
+#define DESIGNCFG_DEBUG12                   0x02AC
+#define AXI_QOS_CFG_0                       0x02E0
+#define AXI_QOS_CFG_1                       0x02E4
+#define AXI_QOS_CFG_2                       0x02E8
+#define AXI_QOS_CFG_3                       0x02EC
+#define INT_Q1_STATUS                       0x0400
+#define INT_Q2_STATUS                       0x0404
+#define INT_Q3_STATUS                       0x0408
+#define INT_Q4_STATUS                       0x040C
+#define INT_Q5_STATUS                       0x0410
+#define INT_Q6_STATUS                       0x0414
+#define INT_Q7_STATUS                       0x0418
+#define INT_Q8_STATUS                       0x041C
+#define INT_Q9_STATUS                       0x0420
+#define INT_Q10_STATUS                      0x0424
+#define INT_Q11_STATUS                      0x0428
+#define INT_Q12_STATUS                      0x042C
+#define INT_Q13_STATUS                      0x0430
+#define INT_Q14_STATUS                      0x0434
+#define INT_Q15_STATUS                      0x0438
+#define TRANSMIT_Q1_PTR                     0x0440
+#define TRANSMIT_Q2_PTR                     0x0444
+#define TRANSMIT_Q3_PTR                     0x0448
+#define TRANSMIT_Q4_PTR                     0x044C
+#define TRANSMIT_Q5_PTR                     0x0450
+#define TRANSMIT_Q6_PTR                     0x0454
+#define TRANSMIT_Q7_PTR                     0x0458
+#define TRANSMIT_Q8_PTR                     0x045C
+#define TRANSMIT_Q9_PTR                     0x0460
+#define TRANSMIT_Q10_PTR                    0x0464
+#define TRANSMIT_Q11_PTR                    0x0468
+#define TRANSMIT_Q12_PTR                    0x046C
+#define TRANSMIT_Q13_PTR                    0x0470
+#define TRANSMIT_Q14_PTR                    0x0474
+#define TRANSMIT_Q15_PTR                    0x0478
+#define RECEIVE_Q1_PTR                      0x0480
+#define RECEIVE_Q2_PTR                      0x0484
+#define RECEIVE_Q3_PTR                      0x0488
+#define RECEIVE_Q4_PTR                      0x048C
+#define RECEIVE_Q5_PTR                      0x0490
+#define RECEIVE_Q6_PTR                      0x0494
+#define RECEIVE_Q7_PTR                      0x0498
+#define DMA_RXBUF_SIZE_Q1                   0x04A0
+#define DMA_RXBUF_SIZE_Q2                   0x04A4
+#define DMA_RXBUF_SIZE_Q3                   0x04A8
+#define DMA_RXBUF_SIZE_Q4                   0x04AC
+#define DMA_RXBUF_SIZE_Q5                   0x04B0
+#define DMA_RXBUF_SIZE_Q6                   0x04B4
+#define DMA_RXBUF_SIZE_Q7                   0x04B8
+#define CBS_CONTROL                         0x04BC
+#define CBS_IDLESLOPE_Q_A                   0x04C0
+#define CBS_IDLESLOPE_Q_B                   0x04C4
+#define UPPER_TX_Q_BASE_ADDR                0x04C8
+#define TX_BD_CONTROL                       0x04CC
+#define RX_BD_CONTROL                       0x04D0
+#define UPPER_RX_Q_BASE_ADDR                0x04D4
+#define WD_COUNTER                          0x04EC
+#define AXI_TX_FULL_THRESH0                 0x04F8
+#define AXI_TX_FULL_THRESH1                 0x04FC
+#define SCREENING_TYPE_1_REGISTER_0         0x0500
+#define SCREENING_TYPE_1_REGISTER_1         0x0504
+#define SCREENING_TYPE_1_REGISTER_2         0x0508
+#define SCREENING_TYPE_1_REGISTER_3         0x050C
+#define SCREENING_TYPE_1_REGISTER_4         0x0510
+#define SCREENING_TYPE_1_REGISTER_5         0x0514
+#define SCREENING_TYPE_1_REGISTER_6         0x0518
+#define SCREENING_TYPE_1_REGISTER_7         0x051C
+#define SCREENING_TYPE_1_REGISTER_8         0x0520
+#define SCREENING_TYPE_1_REGISTER_9         0x0524
+#define SCREENING_TYPE_1_REGISTER_10        0x0528
+#define SCREENING_TYPE_1_REGISTER_11        0x052C
+#define SCREENING_TYPE_1_REGISTER_12        0x0530
+#define SCREENING_TYPE_1_REGISTER_13        0x0534
+#define SCREENING_TYPE_1_REGISTER_14        0x0538
+#define SCREENING_TYPE_1_REGISTER_15        0x053C
+#define SCREENING_TYPE_2_REGISTER_0         0x0540
+#define SCREENING_TYPE_2_REGISTER_1         0x0544
+#define SCREENING_TYPE_2_REGISTER_2         0x0548
+#define SCREENING_TYPE_2_REGISTER_3         0x054C
+#define SCREENING_TYPE_2_REGISTER_4         0x0550
+#define SCREENING_TYPE_2_REGISTER_5         0x0554
+#define SCREENING_TYPE_2_REGISTER_6         0x0558
+#define SCREENING_TYPE_2_REGISTER_7         0x055C
+#define SCREENING_TYPE_2_REGISTER_8         0x0560
+#define SCREENING_TYPE_2_REGISTER_9         0x0564
+#define SCREENING_TYPE_2_REGISTER_10        0x0568
+#define SCREENING_TYPE_2_REGISTER_11        0x056C
+#define SCREENING_TYPE_2_REGISTER_12        0x0570
+#define SCREENING_TYPE_2_REGISTER_13        0x0574
+#define SCREENING_TYPE_2_REGISTER_14        0x0578
+#define SCREENING_TYPE_2_REGISTER_15        0x057C
+#define TX_SCHED_CTRL                       0x0580
+#define BW_RATE_LIMIT_Q0TO3                 0x0590
+#define BW_RATE_LIMIT_Q4TO7                 0x0594
+#define BW_RATE_LIMIT_Q8TO11                0x0598
+#define BW_RATE_LIMIT_Q12TO15               0x059C
+#define TX_Q_SEG_ALLOC_Q_LOWER              0x05A0
+#define TX_Q_SEG_ALLOC_Q_UPPER              0x05A4
+#define RECEIVE_Q8_PTR                      0x05C0
+#define RECEIVE_Q9_PTR                      0x05C4
+#define RECEIVE_Q10_PTR                     0x05C8
+#define RECEIVE_Q11_PTR                     0x05CC
+#define RECEIVE_Q12_PTR                     0x05D0
+#define RECEIVE_Q13_PTR                     0x05D4
+#define RECEIVE_Q14_PTR                     0x05D8
+#define RECEIVE_Q15_PTR                     0x05DC
+#define DMA_RXBUF_SIZE_Q8                   0x05E0
+#define DMA_RXBUF_SIZE_Q9                   0x05E4
+#define DMA_RXBUF_SIZE_Q10                  0x05E8
+#define DMA_RXBUF_SIZE_Q11                  0x05EC
+#define DMA_RXBUF_SIZE_Q12                  0x05F0
+#define DMA_RXBUF_SIZE_Q13                  0x05F4
+#define DMA_RXBUF_SIZE_Q14                  0x05F8
+#define DMA_RXBUF_SIZE_Q15                  0x05FC
+#define INT_Q1_ENABLE                       0x0600
+#define INT_Q2_ENABLE                       0x0604
+#define INT_Q3_ENABLE                       0x0608
+#define INT_Q4_ENABLE                       0x060C
+#define INT_Q5_ENABLE                       0x0610
+#define INT_Q6_ENABLE                       0x0614
+#define INT_Q7_ENABLE                       0x0618
+#define INT_Q1_DISABLE                      0x0620
+#define INT_Q2_DISABLE                      0x0624
+#define INT_Q3_DISABLE                      0x0628
+#define INT_Q4_DISABLE                      0x062C
+#define INT_Q5_DISABLE                      0x0630
+#define INT_Q6_DISABLE                      0x0634
+#define INT_Q7_DISABLE                      0x0638
+#define INT_Q1_MASK                         0x0640
+#define INT_Q2_MASK                         0x0644
+#define INT_Q3_MASK                         0x0648
+#define INT_Q4_MASK                         0x064C
+#define INT_Q5_MASK                         0x0650
+#define INT_Q6_MASK                         0x0654
+#define INT_Q7_MASK                         0x0658
+#define INT_Q8_ENABLE                       0x0660
+#define INT_Q9_ENABLE                       0x0664
+#define INT_Q10_ENABLE                      0x0668
+#define INT_Q11_ENABLE                      0x066C
+#define INT_Q12_ENABLE                      0x0670
+#define INT_Q13_ENABLE                      0x0674
+#define INT_Q14_ENABLE                      0x0678
+#define INT_Q15_ENABLE                      0x067C
+#define INT_Q8_DISABLE                      0x0680
+#define INT_Q9_DISABLE                      0x0684
+#define INT_Q10_DISABLE                     0x0688
+#define INT_Q11_DISABLE                     0x068C
+#define INT_Q12_DISABLE                     0x0690
+#define INT_Q13_DISABLE                     0x0694
+#define INT_Q14_DISABLE                     0x0698
+#define INT_Q15_DISABLE                     0x069C
+#define INT_Q8_MASK                         0x06A0
+#define INT_Q9_MASK                         0x06A4
+#define INT_Q10_MASK                        0x06A8
+#define INT_Q11_MASK                        0x06AC
+#define INT_Q12_MASK                        0x06B0
+#define INT_Q13_MASK                        0x06B4
+#define INT_Q14_MASK                        0x06B8
+#define INT_Q15_MASK                        0x06BC
+#define SCREENING_TYPE_2_ETHERTYPE_REG_0    0x06E0
+#define SCREENING_TYPE_2_ETHERTYPE_REG_1    0x06E4
+#define SCREENING_TYPE_2_ETHERTYPE_REG_2    0x06E8
+#define SCREENING_TYPE_2_ETHERTYPE_REG_3    0x06EC
+#define SCREENING_TYPE_2_ETHERTYPE_REG_4    0x06F0
+#define SCREENING_TYPE_2_ETHERTYPE_REG_5    0x06F4
+#define SCREENING_TYPE_2_ETHERTYPE_REG_6    0x06F8
+#define SCREENING_TYPE_2_ETHERTYPE_REG_7    0x06FC
+#define TYPE2_COMPARE_0_WORD_0              0x0700
+#define TYPE2_COMPARE_0_WORD_1              0x0704
+#define TYPE2_COMPARE_1_WORD_0              0x0708
+#define TYPE2_COMPARE_1_WORD_1              0x070C
+#define TYPE2_COMPARE_2_WORD_0              0x0710
+#define TYPE2_COMPARE_2_WORD_1              0x0714
+#define TYPE2_COMPARE_3_WORD_0              0x0718
+#define TYPE2_COMPARE_3_WORD_1              0x071C
+#define TYPE2_COMPARE_4_WORD_0              0x0720
+#define TYPE2_COMPARE_4_WORD_1              0x0724
+#define TYPE2_COMPARE_5_WORD_0              0x0728
+#define TYPE2_COMPARE_5_WORD_1              0x072C
+#define TYPE2_COMPARE_6_WORD_0              0x0730
+#define TYPE2_COMPARE_6_WORD_1              0x0734
+#define TYPE2_COMPARE_7_WORD_0              0x0738
+#define TYPE2_COMPARE_7_WORD_1              0x073C
+#define TYPE2_COMPARE_8_WORD_0              0x0740
+#define TYPE2_COMPARE_8_WORD_1              0x0744
+#define TYPE2_COMPARE_9_WORD_0              0x0748
+#define TYPE2_COMPARE_9_WORD_1              0x074C
+#define TYPE2_COMPARE_10_WORD_0             0x0750
+#define TYPE2_COMPARE_10_WORD_1             0x0754
+#define TYPE2_COMPARE_11_WORD_0             0x0758
+#define TYPE2_COMPARE_11_WORD_1             0x075C
+#define TYPE2_COMPARE_12_WORD_0             0x0760
+#define TYPE2_COMPARE_12_WORD_1             0x0764
+#define TYPE2_COMPARE_13_WORD_0             0x0768
+#define TYPE2_COMPARE_13_WORD_1             0x076C
+#define TYPE2_COMPARE_14_WORD_0             0x0770
+#define TYPE2_COMPARE_14_WORD_1             0x0774
+#define TYPE2_COMPARE_15_WORD_0             0x0778
+#define TYPE2_COMPARE_15_WORD_1             0x077C
+#define TYPE2_COMPARE_16_WORD_0             0x0780
+#define TYPE2_COMPARE_16_WORD_1             0x0784
+#define TYPE2_COMPARE_17_WORD_0             0x0788
+#define TYPE2_COMPARE_17_WORD_1             0x078C
+#define TYPE2_COMPARE_18_WORD_0             0x0790
+#define TYPE2_COMPARE_18_WORD_1             0x0794
+#define TYPE2_COMPARE_19_WORD_0             0x0798
+#define TYPE2_COMPARE_19_WORD_1             0x079C
+#define TYPE2_COMPARE_20_WORD_0             0x07A0
+#define TYPE2_COMPARE_20_WORD_1             0x07A4
+#define TYPE2_COMPARE_21_WORD_0             0x07A8
+#define TYPE2_COMPARE_21_WORD_1             0x07AC
+#define TYPE2_COMPARE_22_WORD_0             0x07B0
+#define TYPE2_COMPARE_22_WORD_1             0x07B4
+#define TYPE2_COMPARE_23_WORD_0             0x07B8
+#define TYPE2_COMPARE_23_WORD_1             0x07BC
+#define TYPE2_COMPARE_24_WORD_0             0x07C0
+#define TYPE2_COMPARE_24_WORD_1             0x07C4
+#define TYPE2_COMPARE_25_WORD_0             0x07C8
+#define TYPE2_COMPARE_25_WORD_1             0x07CC
+#define TYPE2_COMPARE_26_WORD_0             0x07D0
+#define TYPE2_COMPARE_26_WORD_1             0x07D4
+#define TYPE2_COMPARE_27_WORD_0             0x07D8
+#define TYPE2_COMPARE_27_WORD_1             0x07DC
+#define TYPE2_COMPARE_28_WORD_0             0x07E0
+#define TYPE2_COMPARE_28_WORD_1             0x07E4
+#define TYPE2_COMPARE_29_WORD_0             0x07E8
+#define TYPE2_COMPARE_29_WORD_1             0x07EC
+#define TYPE2_COMPARE_30_WORD_0             0x07F0
+#define TYPE2_COMPARE_30_WORD_1             0x07F4
+#define TYPE2_COMPARE_31_WORD_0             0x07F8
+#define TYPE2_COMPARE_31_WORD_1             0x07FC
+#define ENST_START_TIME_Q8                  0x0800
+#define ENST_START_TIME_Q9                  0x0804
+#define ENST_START_TIME_Q10                 0x0808
+#define ENST_START_TIME_Q11                 0x080C
+#define ENST_START_TIME_Q12                 0x0810
+#define ENST_START_TIME_Q13                 0x0814
+#define ENST_START_TIME_Q14                 0x0818
+#define ENST_START_TIME_Q15                 0x081C
+#define ENST_ON_TIME_Q8                     0x0820
+#define ENST_ON_TIME_Q9                     0x0824
+#define ENST_ON_TIME_Q10                    0x0828
+#define ENST_ON_TIME_Q11                    0x082C
+#define ENST_ON_TIME_Q12                    0x0830
+#define ENST_ON_TIME_Q13                    0x0834
+#define ENST_ON_TIME_Q14                    0x0838
+#define ENST_ON_TIME_Q15                    0x083C
+#define ENST_OFF_TIME_Q8                    0x0840
+#define ENST_OFF_TIME_Q9                    0x0844
+#define ENST_OFF_TIME_Q10                   0x0848
+#define ENST_OFF_TIME_Q11                   0x084C
+#define ENST_OFF_TIME_Q12                   0x0850
+#define ENST_OFF_TIME_Q13                   0x0854
+#define ENST_OFF_TIME_Q14                   0x0858
+#define ENST_OFF_TIME_Q15                   0x085C
+#define ENST_CONTROL                        0x0880
+#define RX_Q0_FLUSH                         0x0B00
+#define RX_Q1_FLUSH                         0x0B04
+#define RX_Q2_FLUSH                         0x0B08
+#define RX_Q3_FLUSH                         0x0B0C
+#define RX_Q4_FLUSH                         0x0B10
+#define RX_Q5_FLUSH                         0x0B14
+#define RX_Q6_FLUSH                         0x0B18
+#define RX_Q7_FLUSH                         0x0B1C
+#define RX_Q8_FLUSH                         0x0B20
+#define RX_Q9_FLUSH                         0x0B24
+#define RX_Q10_FLUSH                        0x0B28
+#define RX_Q11_FLUSH                        0x0B2C
+#define RX_Q12_FLUSH                        0x0B30
+#define RX_Q13_FLUSH                        0x0B34
+#define RX_Q14_FLUSH                        0x0B38
+#define RX_Q15_FLUSH                        0x0B3C
+#define SCR2_REG0_RATE_LIMIT                0x0B40
+#define SCR2_REG1_RATE_LIMIT                0x0B44
+#define SCR2_REG2_RATE_LIMIT                0x0B48
+#define SCR2_REG3_RATE_LIMIT                0x0B4C
+#define SCR2_REG4_RATE_LIMIT                0x0B50
+#define SCR2_REG5_RATE_LIMIT                0x0B54
+#define SCR2_REG6_RATE_LIMIT                0x0B58
+#define SCR2_REG7_RATE_LIMIT                0x0B5C
+#define SCR2_REG8_RATE_LIMIT                0x0B60
+#define SCR2_REG9_RATE_LIMIT                0x0B64
+#define SCR2_REG10_RATE_LIMIT               0x0B68
+#define SCR2_REG11_RATE_LIMIT               0x0B6C
+#define SCR2_REG12_RATE_LIMIT               0x0B70
+#define SCR2_REG13_RATE_LIMIT               0x0B74
+#define SCR2_REG14_RATE_LIMIT               0x0B78
+#define SCR2_REG15_RATE_LIMIT               0x0B7C
+#define SCR2_RATE_STATUS                    0x0B80
+#define ASF_INT_STATUS                      0x0E00
+#define ASF_INT_RAW_STATUS                  0x0E04
+#define ASF_INT_MASK                        0x0E08
+#define ASF_INT_TEST                        0x0E0C
+#define ASF_FATAL_NONFATAL_SELECT           0x0E10
+#define ASF_TRANS_TO_FAULT_MASK             0x0E34
+#define ASF_TRANS_TO_FAULT_STATUS           0x0E38
+#define ASF_PROTOCOL_FAULT_MASK             0x0E40
+#define ASF_PROTOCOL_FAULT_STATUS           0x0E44
+
+/* Register bit field definitions *******************************************/
+
+/* NETWORK_CONTROL:
+ * The network control register contains general MAC control functions
+ * for both receiver and transmitter.
+ */
+
+#define NETWORK_CONTROL_LOOPBACK                                (1 << 0)
+#define NETWORK_CONTROL_LOOPBACK_LOCAL                          (1 << 1)
+#define NETWORK_CONTROL_ENABLE_RECEIVE                          (1 << 2)
+#define NETWORK_CONTROL_ENABLE_TRANSMIT                         (1 << 3)
+#define NETWORK_CONTROL_MAN_PORT_EN                             (1 << 4)
+#define NETWORK_CONTROL_CLEAR_ALL_STATS_REGS                    (1 << 5)
+#define NETWORK_CONTROL_INC_ALL_STATS_REGS                      (1 << 6)
+#define NETWORK_CONTROL_STATS_WRITE_EN                          (1 << 7)
+#define NETWORK_CONTROL_BACK_PRESSURE                           (1 << 8)
+#define NETWORK_CONTROL_TRANSMIT_START                          (1 << 9)
+#define NETWORK_CONTROL_TRANSMIT_HALT                           (1 << 10)
+#define NETWORK_CONTROL_TX_PAUSE_FRAME_REQ                      (1 << 11)
+#define NETWORK_CONTROL_TX_PAUSE_FRAME_ZERO                     (1 << 12)
+#define NETWORK_CONTROL_STORE_RX_TS                             (1 << 15)
+#define NETWORK_CONTROL_PFC_ENABLE                              (1 << 16)
+#define NETWORK_CONTROL_TRANSMIT_PFC_PRIORITY_BASED_PAUSE_FRAME (1 << 17)
+#define NETWORK_CONTROL_FLUSH_RX_PKT_PCLK                       (1 << 18)
+#define NETWORK_CONTROL_TX_LPI_EN                               (1 << 19)
+#define NETWORK_CONTROL_PTP_UNICAST_ENA                         (1 << 20)
+#define NETWORK_CONTROL_ALT_SGMII_MODE                          (1 << 21)
+#define NETWORK_CONTROL_STORE_UDP_OFFSET                        (1 << 22)
+#define NETWORK_CONTROL_EXT_TSU_PORT_ENABLE                     (1 << 23)
+#define NETWORK_CONTROL_ONE_STEP_SYNC_MODE                      (1 << 24)
+#define NETWORK_CONTROL_PFC_CTRL                                (1 << 25)
+#define NETWORK_CONTROL_EXT_RXQ_SEL_EN                          (1 << 26)
+#define NETWORK_CONTROL_OSS_CORRECTION_FIELD                    (1 << 27)
+#define NETWORK_CONTROL_SEL_MII_ON_RGMII                        (1 << 28)
+#define NETWORK_CONTROL_TWO_PT_FIVE_GIG                         (1 << 29)
+#define NETWORK_CONTROL_IFG_EATS_QAV_CREDIT                     (1 << 30)
+
+/* NETWORK_CONFIG:
+ * The network configuration register contains functions for
+ * setting the mode of operation for the Gigabit Ethernet MAC.
+ */
+
+#define NETWORK_CONFIG_SPEED                                    (1 << 0)
+#define NETWORK_CONFIG_FULL_DUPLEX                              (1 << 1)
+#define NETWORK_CONFIG_DISCARD_NON_VLAN_FRAMES                  (1 << 2)
+#define NETWORK_CONFIG_JUMBO_FRAMES                             (1 << 3)
+#define NETWORK_CONFIG_COPY_ALL_FRAMES                          (1 << 4)
+#define NETWORK_CONFIG_NO_BROADCAST                             (1 << 5)
+#define NETWORK_CONFIG_MULTICAST_HASH_ENABLE                    (1 << 6)
+#define NETWORK_CONFIG_UNICAST_HASH_ENABLE                      (1 << 7)
+#define NETWORK_CONFIG_RECEIVE_1536_BYTE_FRAMES                 (1 << 8)
+#define NETWORK_CONFIG_EXTERNAL_ADDRESS_MATCH_ENABLE            (1 << 9)
+#define NETWORK_CONFIG_GIGABIT_MODE_ENABLE                      (1 << 10)
+#define NETWORK_CONFIG_PCS_SELECT                               (1 << 11)
+#define NETWORK_CONFIG_RETRY_TEST                               (1 << 12)
+#define NETWORK_CONFIG_PAUSE_ENABLE                             (1 << 13)
+#define NETWORK_CONFIG_RECEIVE_BUFFER_OFFSET_MASK               (3)
+#define NETWORK_CONFIG_RECEIVE_BUFFER_OFFSET_SHIFT              (14)
+#define NETWORK_CONFIG_LENGTH_FIELD_ERROR_FRAME_DISCARD         (1 << 16)
+#define NETWORK_CONFIG_FCS_REMOVE                               (1 << 17)
+#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT                  (18)
+#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK                   (7)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8                    (0 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16                   (1 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32                   (2 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48                   (3 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64                   (4 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96                   (5 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT                     (21)
+#define NETWORK_CONFIG_DATA_BUS_WIDTH_MASK                      (3)
+#define NETWORK_CONFIG_DISABLE_COPY_OF_PAUSE_FRAMES             (1 << 23)
+#define NETWORK_CONFIG_RECEIVE_CHECKSUM_OFFLOAD_ENABLE          (1 << 24)
+#define NETWORK_CONFIG_EN_HALF_DUPLEX_RX                        (1 << 25)
+#define NETWORK_CONFIG_IGNORE_RX_FCS                            (1 << 26)
+#define NETWORK_CONFIG_SGMII_MODE_ENABLE                        (1 << 27)
+#define NETWORK_CONFIG_IPG_STRETCH_ENABLE                       (1 << 28)
+#define NETWORK_CONFIG_NSP_CHANGE                               (1 << 29)
+#define NETWORK_CONFIG_IGNORE_IPG_RX_ER                         (1 << 30)
+#define NETWORK_CONFIG_UNI_DIRECTION_ENABLE                     (1 << 31)
+
+/* NETWORK_STATUS:
+ * The network status register returns status information with respect
+ * to the PHY management MDIO interface, the PCS, priority flow control,
+ * LPI and other status.
+ */
+
+#define NETWORK_STATUS_PCS_LINK_STATE                           (1 << 0)
+#define NETWORK_STATUS_MDIO_IN                                  (1 << 1)
+#define NETWORK_STATUS_MAN_DONE                                 (1 << 2)
+#define NETWORK_STATUS_MAC_FULL_DUPLEX                          (1 << 3)
+#define NETWORK_STATUS_MAC_PAUSE_RX_EN                          (1 << 4)
+#define NETWORK_STATUS_MAC_PAUSE_TX_EN                          (1 << 5)
+#define NETWORK_STATUS_PFC_NEGOTIATE_PCLK                       (1 << 6)
+#define NETWORK_STATUS_LPI_INDICATE_PCLK                        (1 << 7)
+#define NETWORK_STATUS_AXI_XACTION_OUTSTANDING                  (1 << 8)
+#define NETWORK_STATUS_LINK_FAULT_INDICATION_SHIFT              (1 << 9)
+#define NETWORK_STATUS_LINK_FAULT_INDICATION_MASK               (7)
+
+/* DMA_CONFIG:
+ * DMA Configuration Register
+ */
+
+#define DMA_CONFIG_AMBA_BURST_LENGTH_SHIFT          (0)       /* Bits: 0-4 */
+#define DMA_CONFIG_AMBA_BURST_LENGTH_MASK           (0x1f)
+#define DMA_CONFIG_HDR_DATA_SPLITTING_EN            (1 << 5)  /* Bit 5 */
+#define DMA_CONFIG_ENDIAN_SWAP_MANAGEMENT           (1 << 6)  /* Bit 6 */
+#define DMA_CONFIG_ENDIAN_SWAP_PACKET               (1 << 7)  /* Bit 7 */
+#define DMA_CONFIG_RX_PBUF_SIZE_SHIFT               (8)       /* Bits: 8-9 */
+#define DMA_CONFIG_RX_PBUF_SIZE_MASK                (3)
+#define DMA_CONFIG_TX_PBUF_SIZE                     (1 << 10) /* Bit 10 */
+#define DMA_CONFIG_TX_PBUF_TCP_EN                   (1 << 11) /* Bit 11 */
+#define DMA_CONFIG_INFINITE_LAST_DBUF_SIZE_EN       (1 << 12) /* Bit 12 */
+#define DMA_CONFIG_CRC_ERROR_REPORT                 (1 << 13) /* Bit 13 */
+#define DMA_CONFIG_RX_BUF_SIZE_MASK                 (0xff)
+#define DMA_CONFIG_RX_BUF_SIZE_SHIFT                (16)
+#define DMA_CONFIG_FORCE_DISCARD_ON_ERR             (1 << 24) /* Bit 24 */
+#define DMA_CONFIG_FORCE_MAX_AMBA_BURST_RX          (1 << 25) /* Bit 25 */
+#define DMA_CONFIG_FORCE_MAX_AMBA_BURST_TX          (1 << 26) /* Bit 26 */
+                                                              /* Bit 27 reserved */
+#define DMA_CONFIG_RX_BD_EXTENDED_MODE_EN           (1 << 28) /* Bit 28 */
+#define DMA_CONFIG_TX_BD_EXTENDED_MODE_EN           (1 << 29) /* Bit 29 */
+#define DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1             (1 << 30) /* Bit 30 */
+                                                              /* Bit 31 reserved */
+
+/* TRANSMIT_STATUS:
+ * This register, when read, provides details of the status
+ * of the transmit path. Once read, individual bits may be cleared
+ * by writing 1 to them. It is not possible to set a bit to 1 by writing
+ * to the register.
+ */
+
+#define TRANSMIT_STATUS_USED_BIT_READ               (1 << 0)
+#define TRANSMIT_STATUS_COLLISION_OCCURED           (1 << 1)
+#define TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED        (1 << 2)
+#define TRANSMIT_STATUS_TRANSMIT_GO                 (1 << 3)
+#define TRANSMIT_STATUS_AMBA_ERROR                  (1 << 4)
+#define TRANSMIT_STATUS_TRANSMIT_COMPLETE           (1 << 5)
+#define TRANSMIT_STATUS_UNDERRUN                    (1 << 6)
+#define TRANSMIT_STATUS_LATE_COLLISION              (1 << 7)
+#define TRANSMIT_STATUS_RESP_NOT_OK                 (1 << 8)
+#define TRANSMIT_STATUS_TX_MAC_LOCKUP               (1 << 9)
+#define TRANSMIT_STATUS_TX_DMA_LOCKUP               (1 << 10)
+
+/* RECEIVE_STATUS:
+ * This register, when read, provides details of the status
+ * of the receive path. Once read, individual bits may be cleared
+ * by writing 1 to them. It is not possible to set a bit to 1 by writing
+ * to the register.
+ */
+#define RECEIVE_STATUS_BUFFER_NOT_AVAILABLE         (1 << 0)
+#define RECEIVE_STATUS_FRAME_RECEIVED               (1 << 1)
+#define RECEIVE_STATUS_RECEIVE_OVERRUN              (1 << 2)
+#define RECEIVE_STATUS_RESP_NOT_OK                  (1 << 3)
+#define RECEIVE_STATUS_RX_MAC_LOCKUP                (1 << 4)
+#define RECEIVE_STATUS_RX_DMA_LOCKUP                (1 << 5)
+
+/* PHY_MANAGEMENT:
+ */
+
+#define PHY_MANAGEMENT_PHY_DATA_SHIFT               (0)
+#define PHY_MANAGEMENT_PHY_DATA_MASK                (0xffff)
+#define PHY_MANAGEMENT_PHY_DATA(n)                  ((n & PHY_MANAGEMENT_PHY_DATA_MASK) << PHY_MANAGEMENT_PHY_DATA_SHIFT)
+#define PHY_MANAGEMENT_WRITE10                      (2 << PHY_MANAGEMENT_WRITE10_SHIFT)
+#define PHY_MANAGEMENT_WRITE10_SHIFT                (16)
+#define PHY_MANAGEMENT_WRITE10_MASK                 (3)
+#define PHY_MANAGEMENT_REG_ADDRESS_SHIFT            (18)
+#define PHY_MANAGEMENT_REG_ADDRESS_MASK             (0x1f)
+#define PHY_MANAGEMENT_REG_ADDRESS(n)               ((n & PHY_MANAGEMENT_REG_ADDRESS_MASK ) << PHY_MANAGEMENT_REG_ADDRESS_SHIFT)
+#define PHY_MANAGEMENT_PHY_ADDRESS_SHIFT            (23)
+#define PHY_MANAGEMENT_PHY_ADDRESS_MASK             (0x1f)
+#define PHY_MANAGEMENT_PHY_ADDRESS(n)               ((n & PHY_MANAGEMENT_PHY_ADDRESS_MASK ) << PHY_MANAGEMENT_PHY_ADDRESS_SHIFT)
+#define PHY_MANAGEMENT_OPERATION_SHIFT              (28)
+#define PHY_MANAGEMENT_OPERATION_MASK               (3)
+#  define PHY_MANAGEMENT_OPERATION_READ             (2 << PHY_MANAGEMENT_OPERATION_SHIFT)
+#  define PHY_MANAGEMENT_OPERATION_WRITE            (1 << PHY_MANAGEMENT_OPERATION_SHIFT)
+#define PHY_MANAGEMENT_WRITE_1                      (1 << 30)
+#define PHY_MANAGEMENT_WRITE_0                      (1 << 31)
+
+/* irq status, enable and disable */
+
+#define GEM_INT_MANAGEMENT_DONE                     (1 << 0)
+#define GEM_INT_RECEIVE_COMPLETE                    (1 << 1)
+#define GEM_INT_RECEIVE_BUFFER_USED_BIT_READ        (1 << 2)
+#define GEM_INT_TRANSMIT_BUFFER_USED_BIT_READ       (1 << 3)
+#define GEM_INT_TRANSMIT_BUFFER_UNDERRUN            (1 << 4)
+#define GEM_INT_RETRY_LIMIT_EXCEEDED                (1 << 5)
+#define GEM_INT_AMBA_ERROR                          (1 << 6)
+#define GEM_INT_TRANSMIT_COMPLETE                   (1 << 7)
+#define GEM_INT_RECEIVE_OVERRUN                     (1 << 10)
+#define GEM_INT_RESP_NOT_OK                         (1 << 11)
+
+/* Receive buffer descriptor:  Address word */
+
+#define GEM_RX_DMA_ADDR_OWNER       (1 << 0)     /* Bit 0:  1=Software owns; 0=GMAC owns */
+#define GEM_RX_DMA_ADDR_WRAP        (1 << 1)     /* Bit 1:  Last descriptor in list */
+#define GEM_RX_DMA_ADDR_MASK        (0xfffffffc) /* Bits 2-31: Aligned buffer address */
+
+/* Receive buffer descriptor:  status word */
+
+#define GEM_RX_DMA_STATUS_FRAME_LEN_SHIFT  (0)         /* Bits 0-12: Frame length */
+#define GEM_RX_DMA_STATUS_FRAME_LEN_MASK   (0x00000fff)
+#define GEM_RX_DMA_STATUS_SOF              (1 << 14)   /* Bit 14: Start of frame */
+#define GEM_RX_DMA_STATUS_EOF              (1 << 15)   /* Bit 14: End of frame */
+
+#define GEM_TX_DMA_LAST                    (1 << 15)  /* the last buffer in a frame */
+#define GEM_TX_DMA_NO_CRC                  (1 << 16)  /* don't calc and append crc */
+                                                      /* 17 - 19 reserved */
+#define GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT    (20)       /* */
+#define GEM_TX_DMA_OFFLOAD_ERRORS_MASK     (7)        /* */
+#define GEM_TX_DMA_OFFLOAD_ERROR_OK        (0 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_VLAN      (1 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_SNAP      (2 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_IP        (3 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_UNK       (4 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_FRAG      (5 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_PROTO     (6 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_END       (7 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_TS_PRESENT              (1 << 23)  /* */
+#define GEM_TX_DMA_LATE_COL_ERROR          (1 << 26)  /* */
+#define GEM_TX_DMA_BUS_ERROR               (1 << 27)  /* */
+#define GEM_TX_DMA_UNDERRUN                (1 << 28)  /* */
+#define GEM_TX_DMA_RETRY_ERROR             (1 << 29)  /* */
+#define GEM_TX_DMA_WRAP                    (1 << 30)  /* */
+#define GEM_TX_DMA_USED                    (1 << 31)  /* */

Review comment:
       Do we need to update comments here?

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);

Review comment:
       ```suggestion
             rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);

Review comment:
       ```suggestion
             nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);

Review comment:
       ```suggestion
                     rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif

Review comment:
       ```suggestion
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;

Review comment:
       ```suggestion
         txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
                                                            GEM_TX_DMA_WRAP;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));

Review comment:
       ```suggestion
             bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;

Review comment:
       ```suggestion
     uint32_t txhead32 = priv->queue[queue].txhead;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);

Review comment:
       ```suggestion
                 rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;

Review comment:
       ```suggestion
             rxdesc[ndx].addr    = lower_32_bits(bufaddr);
             rxdesc[ndx].status  = 0;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);

Review comment:
       ```suggestion
                     rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)

Review comment:
       ```suggestion
     if (priv->queue[queue].txtail > txhead32)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);

Review comment:
       ```suggestion
     ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))

Review comment:
       ```suggestion
     if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;

Review comment:
       I think that volatile is not needed here. Maybe added just to get some pretty minor delay.

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)

Review comment:
       ```suggestion
                  (lpa & GMII_LPA_10BASETXFULL) != 0)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)

Review comment:
       ```suggestion
     if (priv->queue[queue].txbuffer != NULL)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab)

Review comment:
       ```suggestion
     if (priv->queue[queue].rx_desc_tab != NULL)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;

Review comment:
       ```suggestion
         *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6

Review comment:
       ```suggestion
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)

Review comment:
       ```suggestion
     if (priv->queue[queue].txbuffer != NULL)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab)

Review comment:
       ```suggestion
     if (priv->queue[queue].rx_desc_tab != NULL)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));

Review comment:
       ```suggestion
             bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;

Review comment:
       ```suggestion
             regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;

Review comment:
       ```suggestion
         *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);

Review comment:
       ```suggestion
     return (uint16_t)(txhead32 - priv->queue[queue].txtail);
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);

Review comment:
       ```suggestion
     ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;

Review comment:
       ```suggestion
             linkmode = NETWORK_CONFIG_FULL_DUPLEX |
                        NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);

Review comment:
       ```suggestion
         ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);

Review comment:
       ```suggestion
     ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)

Review comment:
       ```suggestion
                  (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)

Review comment:
       ```suggestion
                  (lpa & GMII_LPA_100BASETXHALF) != 0)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_10BASETXHALF) != 0)

Review comment:
       ```suggestion
                  (lpa & GMII_LPA_10BASETXHALF) != 0)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == (void *)MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == (void *)MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)(1UL)) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS (values from baremetal driver) */
+
+  mac_putreg(priv, PCS_CONTROL, 0x8000);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config =  (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                 DMA_CONFIG_TX_PBUF_SIZE |
+                 (((uint32_t)(0x3ul)) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                 ((uint32_t)(0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr;
+  r &= ~1;

Review comment:
       ```suggestion
     uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == (void *)MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == (void *)MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)(1UL)) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS (values from baremetal driver) */
+
+  mac_putreg(priv, PCS_CONTROL, 0x8000);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config =  (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                 DMA_CONFIG_TX_PBUF_SIZE |
+                 (((uint32_t)(0x3ul)) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                 ((uint32_t)(0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));

Review comment:
       ```suggestion
     dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
                   DMA_CONFIG_TX_PBUF_SIZE |
                   (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
                   (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == (void *)MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == (void *)MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)(1UL)) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS (values from baremetal driver) */
+
+  mac_putreg(priv, PCS_CONTROL, 0x8000);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config =  (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                 DMA_CONFIG_TX_PBUF_SIZE |
+                 (((uint32_t)(0x3ul)) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                 ((uint32_t)(0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr;
+  r &= ~1;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif
+{
+  struct mpfs_ethmac_s *priv;
+  int ret = OK;
+  uintptr_t base;
+
+  ninfo("intf: %d\n", intf);
+
+  /* Get the interface structure associated with this interface number. */
+
+  DEBUGASSERT(intf < MPFS_NETHERNET);
+  priv = &g_mpfsethmac[intf];
+
+  /* Initialize the driver structure */
+
+  memset(priv, 0, sizeof(struct mpfs_ethmac_s));
+  priv->dev.d_buf     = g_pktbuf;       /* Single packet buffer */
+  priv->dev.d_ifup    = mpfs_ifup;      /* I/F up (new IP address) callback */
+  priv->dev.d_ifdown  = mpfs_ifdown;    /* I/F down callback */
+  priv->dev.d_txavail = mpfs_txavail;   /* New TX data callback */
+#ifdef CONFIG_NET_MCASTGROUP
+  priv->dev.d_addmac  = mpfs_addmac;    /* Add multicast MAC address */
+  priv->dev.d_rmmac   = mpfs_rmmac;     /* Remove multicast MAC address */
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+  priv->dev.d_ioctl   = mpfs_ioctl;     /* Support PHY ioctl() calls */
+#endif
+  priv->dev.d_private = priv;           /* Used to recover private state */
+  priv->intf          = intf;           /* Remember the interface number */
+  priv->regbase       = g_regbases[intf];
+  priv->mac_q_int[0]  = g_irq_numbers[intf][0];
+  priv->mac_q_int[1]  = g_irq_numbers[intf][1];
+  priv->mac_q_int[2]  = g_irq_numbers[intf][2];
+  priv->mac_q_int[3]  = g_irq_numbers[intf][3];
+  ninfo("mac @ 0x%" PRIx64 "\n", priv->regbase);
+
+  base = priv->regbase;
+  priv->queue[0].int_status     = (uint32_t *)(base + INT_STATUS);
+  priv->queue[1].int_status     = (uint32_t *)(base + INT_Q1_STATUS);
+  priv->queue[2].int_status     = (uint32_t *)(base + INT_Q2_STATUS);
+  priv->queue[3].int_status     = (uint32_t *)(base + INT_Q3_STATUS);
+  priv->queue[0].int_mask       = (uint32_t *)(base + INT_MASK);
+  priv->queue[1].int_mask       = (uint32_t *)(base + INT_Q1_MASK);
+  priv->queue[2].int_mask       = (uint32_t *)(base + INT_Q2_MASK);
+  priv->queue[3].int_mask       = (uint32_t *)(base + INT_Q3_MASK);
+  priv->queue[0].int_enable     = (uint32_t *)(base + INT_ENABLE);
+  priv->queue[1].int_enable     = (uint32_t *)(base + INT_Q1_ENABLE);
+  priv->queue[2].int_enable     = (uint32_t *)(base + INT_Q2_ENABLE);
+  priv->queue[3].int_enable     = (uint32_t *)(base + INT_Q3_ENABLE);
+  priv->queue[0].int_disable    = (uint32_t *)(base + INT_DISABLE);
+  priv->queue[1].int_disable    = (uint32_t *)(base + INT_Q1_DISABLE);
+  priv->queue[2].int_disable    = (uint32_t *)(base + INT_Q2_DISABLE);
+  priv->queue[3].int_disable    = (uint32_t *)(base + INT_Q3_DISABLE);
+  priv->queue[0].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q_PTR);
+  priv->queue[1].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q1_PTR);
+  priv->queue[2].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q2_PTR);
+  priv->queue[3].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q3_PTR);
+  priv->queue[0].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q_PTR);
+  priv->queue[1].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q1_PTR);
+  priv->queue[2].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q2_PTR);
+  priv->queue[3].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q3_PTR);
+  priv->queue[0].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q1);
+  priv->queue[1].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q1);
+  priv->queue[2].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q2);
+  priv->queue[3].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q3);
+
+  /* MPU hack for ETH DMA if not enabled by bootloader */
+
+#ifdef CONFIG_MPFS_MPU_DMA_ENABLE
+#  ifdef CONFIG_MPFS_ETHMAC_0
+  putreg64(0x1f00000fffffffff, MPFS_PMPCFG_ETH0_0);
+#  endif
+#  ifdef CONFIG_MPFS_ETHMAC_1
+  putreg64(0x1f00000fffffffff, MPFS_PMPCFG_ETH1_0);
+#  endif
+#endif
+
+  /* Allocate buffers */
+
+  ret = mpfs_buffer_initialize(priv, 0);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_buffer_initialize failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Attach the IRQ to the driver */
+
+  if (irq_attach(priv->mac_q_int[0], mpfs_interrupt_0, priv))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  if (irq_attach(priv->mac_q_int[1], mpfs_interrupt_1, NULL))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  if (irq_attach(priv->mac_q_int[2], mpfs_interrupt_2, NULL))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  if (irq_attach(priv->mac_q_int[3], mpfs_interrupt_3, NULL))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  /* Enable clocking to the GMAC peripheral (just for mpfs_ifdown()) */
+
+  /* MAC HW clock enable and reset */
+
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET, 0,
+                  SYSREG_SUBBLK_CLOCK_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+
+      for (volatile int i = 0; i < 10000; i++)
+        {
+          ;
+        }
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET, 0,
+                  SYSREG_SUBBLK_CLOCK_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+
+      for (volatile int i = 0; i < 10000; i++)
+        {
+          ;
+        }
+

Review comment:
       can we use `up_udelay` instead?

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == (void *)MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == (void *)MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)(1UL)) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |

Review comment:
       ```suggestion
     net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  (void)irq;
+  (void)context;
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~(GEM_RX_DMA_ADDR_OWNER);
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+            nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |=  NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE]));
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                       GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1U;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE]));
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr   = lower_32_bits(bufaddr);
+          rxdesc[ndx].status = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1U;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = (uint32_t)priv->queue[queue].txhead;
+  if ((uint32_t)priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - (uint32_t)priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if (phyid1 != 0xffff && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr,
+                      GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr,
+                     GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr,
+                         GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                    NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+              (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+              (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+              (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == (void *)MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == (void *)MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)(1UL)) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS (values from baremetal driver) */
+
+  mac_putreg(priv, PCS_CONTROL, 0x8000);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config =  (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                 DMA_CONFIG_TX_PBUF_SIZE |
+                 (((uint32_t)(0x3ul)) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                 ((uint32_t)(0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr;
+  r &= ~1;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif
+{
+  struct mpfs_ethmac_s *priv;
+  int ret = OK;
+  uintptr_t base;
+
+  ninfo("intf: %d\n", intf);
+
+  /* Get the interface structure associated with this interface number. */
+
+  DEBUGASSERT(intf < MPFS_NETHERNET);
+  priv = &g_mpfsethmac[intf];
+
+  /* Initialize the driver structure */
+
+  memset(priv, 0, sizeof(struct mpfs_ethmac_s));
+  priv->dev.d_buf     = g_pktbuf;       /* Single packet buffer */
+  priv->dev.d_ifup    = mpfs_ifup;      /* I/F up (new IP address) callback */
+  priv->dev.d_ifdown  = mpfs_ifdown;    /* I/F down callback */
+  priv->dev.d_txavail = mpfs_txavail;   /* New TX data callback */
+#ifdef CONFIG_NET_MCASTGROUP
+  priv->dev.d_addmac  = mpfs_addmac;    /* Add multicast MAC address */
+  priv->dev.d_rmmac   = mpfs_rmmac;     /* Remove multicast MAC address */
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+  priv->dev.d_ioctl   = mpfs_ioctl;     /* Support PHY ioctl() calls */
+#endif
+  priv->dev.d_private = priv;           /* Used to recover private state */
+  priv->intf          = intf;           /* Remember the interface number */
+  priv->regbase       = g_regbases[intf];
+  priv->mac_q_int[0]  = g_irq_numbers[intf][0];
+  priv->mac_q_int[1]  = g_irq_numbers[intf][1];
+  priv->mac_q_int[2]  = g_irq_numbers[intf][2];
+  priv->mac_q_int[3]  = g_irq_numbers[intf][3];
+  ninfo("mac @ 0x%" PRIx64 "\n", priv->regbase);
+
+  base = priv->regbase;
+  priv->queue[0].int_status     = (uint32_t *)(base + INT_STATUS);
+  priv->queue[1].int_status     = (uint32_t *)(base + INT_Q1_STATUS);
+  priv->queue[2].int_status     = (uint32_t *)(base + INT_Q2_STATUS);
+  priv->queue[3].int_status     = (uint32_t *)(base + INT_Q3_STATUS);
+  priv->queue[0].int_mask       = (uint32_t *)(base + INT_MASK);
+  priv->queue[1].int_mask       = (uint32_t *)(base + INT_Q1_MASK);
+  priv->queue[2].int_mask       = (uint32_t *)(base + INT_Q2_MASK);
+  priv->queue[3].int_mask       = (uint32_t *)(base + INT_Q3_MASK);
+  priv->queue[0].int_enable     = (uint32_t *)(base + INT_ENABLE);
+  priv->queue[1].int_enable     = (uint32_t *)(base + INT_Q1_ENABLE);
+  priv->queue[2].int_enable     = (uint32_t *)(base + INT_Q2_ENABLE);
+  priv->queue[3].int_enable     = (uint32_t *)(base + INT_Q3_ENABLE);
+  priv->queue[0].int_disable    = (uint32_t *)(base + INT_DISABLE);
+  priv->queue[1].int_disable    = (uint32_t *)(base + INT_Q1_DISABLE);
+  priv->queue[2].int_disable    = (uint32_t *)(base + INT_Q2_DISABLE);
+  priv->queue[3].int_disable    = (uint32_t *)(base + INT_Q3_DISABLE);
+  priv->queue[0].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q_PTR);
+  priv->queue[1].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q1_PTR);
+  priv->queue[2].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q2_PTR);
+  priv->queue[3].rx_q_ptr       = (uint32_t *)(base + RECEIVE_Q3_PTR);
+  priv->queue[0].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q_PTR);
+  priv->queue[1].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q1_PTR);
+  priv->queue[2].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q2_PTR);
+  priv->queue[3].tx_q_ptr       = (uint32_t *)(base + TRANSMIT_Q3_PTR);
+  priv->queue[0].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q1);
+  priv->queue[1].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q1);
+  priv->queue[2].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q2);
+  priv->queue[3].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q3);
+
+  /* MPU hack for ETH DMA if not enabled by bootloader */
+
+#ifdef CONFIG_MPFS_MPU_DMA_ENABLE
+#  ifdef CONFIG_MPFS_ETHMAC_0
+  putreg64(0x1f00000fffffffff, MPFS_PMPCFG_ETH0_0);
+#  endif
+#  ifdef CONFIG_MPFS_ETHMAC_1
+  putreg64(0x1f00000fffffffff, MPFS_PMPCFG_ETH1_0);
+#  endif
+#endif
+
+  /* Allocate buffers */
+
+  ret = mpfs_buffer_initialize(priv, 0);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_buffer_initialize failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Attach the IRQ to the driver */
+
+  if (irq_attach(priv->mac_q_int[0], mpfs_interrupt_0, priv))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  if (irq_attach(priv->mac_q_int[1], mpfs_interrupt_1, NULL))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  if (irq_attach(priv->mac_q_int[2], mpfs_interrupt_2, NULL))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  if (irq_attach(priv->mac_q_int[3], mpfs_interrupt_3, NULL))
+    {
+      /* We could not attach the ISR to the interrupt */
+
+      return -EAGAIN;
+    }
+
+  /* Enable clocking to the GMAC peripheral (just for mpfs_ifdown()) */
+
+  /* MAC HW clock enable and reset */
+
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET, 0,
+                  SYSREG_SUBBLK_CLOCK_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+
+      for (volatile int i = 0; i < 10000; i++)
+        {
+          ;
+        }

Review comment:
       can we use `up_udelay` instead?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829407815



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))

Review comment:
       Thank you for explanation




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829869162



##########
File path: Documentation/platforms/risc-v/mpfs/index.rst
##########
@@ -64,17 +64,18 @@ The following list indicates the state of peripherals' support in NuttX:
 ============   =======  =====
 Peripheral     Support  NOTES
 ============   =======  =====
-GPIO           Yes      
+GPIO           Yes
 MMUART         Yes      Uart mode only
-SPI            Yes      
-I2C            Yes      
+SPI            Yes
+I2C            Yes
 eMMC SD/SDIO   Yes      No PHY training
+USB            Yes
+Ethernet MAC   Yes
 Timers         No
-Watchdog       No       
-RTC            No       
-CAN            No       
-eNVM           No       
-USB            No

Review comment:
       USB should work?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jlaitine commented on pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jlaitine commented on pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#issuecomment-1072306779


   Tested also on aries m100pfs , seems to be working great


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829876353



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3823 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;    /* Buffer address */
+    uint32_t status;  /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi; /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;    /* Buffer address */
+    uint32_t status;  /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi; /* Buffer address high 32-bits */
+    uint32_t unused;  /*  */

Review comment:
       Oh... now i got what you mean, sorry about that.
   done.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829070796



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       Yes.
   This is preferred way to disable call to riscv_netinitialize() on early boot.
   is CONFIG_NETDEV_LATEINIT is defined then riscv_netinitialize() is defined as empty.
   exactly same as on any arm targets.
   I will add the mpfs_ethernet.h file for prototypes.

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       Yes.
   This is preferred way to disable call to riscv_netinitialize() on early boot.
   if CONFIG_NETDEV_LATEINIT is defined then riscv_netinitialize() is defined as empty.
   exactly same as on any arm targets.
   I will add the mpfs_ethernet.h file for prototypes.

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))

Review comment:
       like in linux kernel:
   A basic shift-right of a 64- or 32-bit quantity.  Use this to suppress the "right shift count >= width of type" warning when that quantity is 32-bits.
   sadly this macro don't exist in any nuttx headrers.

##########
File path: Documentation/platforms/risc-v/mpfs/index.rst
##########
@@ -64,17 +64,18 @@ The following list indicates the state of peripherals' support in NuttX:
 ============   =======  =====
 Peripheral     Support  NOTES
 ============   =======  =====
-GPIO           Yes      
+GPIO           Yes
 MMUART         Yes      Uart mode only
-SPI            Yes      
-I2C            Yes      
+SPI            Yes
+I2C            Yes
 eMMC SD/SDIO   Yes      No PHY training
+USB            Yes
+Ethernet MAC   Yes
 Timers         No
-Watchdog       No       
-RTC            No       
-CAN            No       
-eNVM           No       
-USB            No

Review comment:
       https://github.com/apache/incubator-nuttx/pull/5740/files/d90ca07a70fbac0ef05afdacc25b724d1ef26157#diff-cfc2aa7229960754dad201acf4a97c917b8ddf85c5ceba9494c10a1bddb24fe9R72
   

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3823 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;    /* Buffer address */
+    uint32_t status;  /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi; /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;    /* Buffer address */
+    uint32_t status;  /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi; /* Buffer address high 32-bits */
+    uint32_t unused;  /*  */

Review comment:
       Oh... now i got what you mean, sorry about that.
   done.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jlaitine commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jlaitine commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829067542



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       I guess that the idea is that by defining CONFIG_NETDEV_LATEINIT you can call mpfs_ethinitialize in you own board files, and the call to the riscv_netinitialize is suppressed in the common code. This is the same as what is done for the arm boards - typically you just initialize it via the common riscv_initialize, but can make an exception by defining CONFIG_NETDEV_LATEINIT and doing your own initialization.
   
   Of course, we could have the header file for these function prototypes (mpfs_ethinitialize and mpfs_phy_boardinitialize) similarly as what is done for arm boards (e.g. stm32_ethernet.h).
   
   
   

##########
File path: boards/risc-v/bl602/bl602evb/configs/wifi/defconfig
##########
@@ -73,6 +73,7 @@ CONFIG_MTD=y
 CONFIG_MTD_PARTITION=y
 CONFIG_NET=y
 CONFIG_NETDB_DNSCLIENT=y
+CONFIG_NETDEV_LATEINIT=y

Review comment:
       since those architectures don't implement the "standard" up/riscv_netinitialize, but they initialize the network in board files directly with some custom interface. 

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       I guess that the idea is that by defining CONFIG_NETDEV_LATEINIT you can call mpfs_ethinitialize in you own board files, and the call to the riscv_netinitialize is suppressed in the common code. This is the same as what is done for the arm boards - typically you just initialize it via the common riscv_initialize, but can make an exception by defining CONFIG_NETDEV_LATEINIT and doing your own initialization.
   
   Of course, we could have a header file for these function prototypes (mpfs_ethinitialize and mpfs_phy_boardinitialize) similarly as what is done for arm boards (e.g. stm32_ethernet.h).
   
   
   




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r825859045



##########
File path: arch/risc-v/src/mpfs/Kconfig
##########
@@ -334,3 +495,6 @@ config ARCH_MPU_HAS_NO4
 
 config ARCH_MPU_HAS_NAPOT
 	default y
+
+
+

Review comment:
       ```suggestion
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN       (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS+1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET		    (1 << 15)
+#  define CORE_RMII_LOOPBACK	  (1 << 2)

Review comment:
       ```suggestion
   #  define CORE_RMII_LOOPBACK	    (1 << 2)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN       (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS+1)

Review comment:
       ```suggestion
   #define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN       (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS+1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET		    (1 << 15)
+#  define CORE_RMII_LOOPBACK	  (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX	(1 << 1)
+#  define CORE_RMII_100MBIT	    (1 << 0)

Review comment:
       ```suggestion
   #  define CORE_RMII_100MBIT	     (1 << 0)
   ```

##########
File path: arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h
##########
@@ -0,0 +1,701 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H
+#define __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/mpfs_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets *********************************************************/
+
+#define NETWORK_CONTROL                     0x0000
+#define NETWORK_CONFIG                      0x0004
+#define NETWORK_STATUS                      0x0008
+#define DMA_CONFIG                          0x0010
+#define TRANSMIT_STATUS                     0x0014
+#define RECEIVE_Q_PTR                       0x0018
+#define TRANSMIT_Q_PTR                      0x001C
+#define RECEIVE_STATUS                      0x0020
+#define INT_STATUS                          0x0024
+#define INT_ENABLE                          0x0028
+#define INT_DISABLE                         0x002C
+#define INT_MASK                            0x0030
+#define PHY_MANAGEMENT                      0x0034
+#define PAUSE_TIME                          0x0038
+#define TX_PAUSE_QUANTUM                    0x003C
+#define PBUF_TXCUTTHRU                      0x0040
+#define PBUF_RXCUTTHRU                      0x0044
+#define JUMBO_MAX_LENGTH                    0x0048
+#define AXI_MAX_PIPELINE                    0x0054
+#define RSC_CONTROL                         0x0058
+#define INT_MODERATION                      0x005C
+#define SYS_WAKE_TIME                       0x0060
+#define LOCKUP_CONFIG                       0x0068
+#define MAC_LOCKUP_TIME                     0x006C
+#define LOCKUP_CONFIG3                      0x0070
+#define RX_WATER_MARK                       0x007C
+#define HASH_BOTTOM                         0x0080
+#define HASH_TOP                            0x0084
+#define SPEC_ADD1_BOTTOM                    0x0088
+#define SPEC_ADD1_TOP                       0x008C
+#define SPEC_ADD2_BOTTOM                    0x0090
+#define SPEC_ADD2_TOP                       0x0094
+#define SPEC_ADD3_BOTTOM                    0x0098
+#define SPEC_ADD3_TOP                       0x009C
+#define SPEC_ADD4_BOTTOM                    0x00A0
+#define SPEC_ADD4_TOP                       0x00A4
+#define SPEC_TYPE1                          0x00A8
+#define SPEC_TYPE2                          0x00AC
+#define SPEC_TYPE3                          0x00B0
+#define SPEC_TYPE4                          0x00B4
+#define WOL_REGISTER                        0x00B8
+#define STRETCH_RATIO                       0x00BC
+#define STACKED_VLAN                        0x00C0
+#define TX_PFC_PAUSE                        0x00C4
+#define MASK_ADD1_BOTTOM                    0x00C8
+#define MASK_ADD1_TOP                       0x00CC
+#define DMA_ADDR_OR_MASK                    0x00D0
+#define RX_PTP_UNICAST                      0x00D4
+#define TX_PTP_UNICAST                      0x00D8
+#define TSU_NSEC_CMP                        0x00DC
+#define TSU_SEC_CMP                         0x00E0
+#define TSU_MSB_SEC_CMP                     0x00E4
+#define TSU_PTP_TX_MSB_SEC                  0x00E8
+#define TSU_PTP_RX_MSB_SEC                  0x00EC
+#define TSU_PEER_TX_MSB_SEC                 0x00F0
+#define TSU_PEER_RX_MSB_SEC                 0x00F4
+#define DPRAM_FILL_DBG                      0x00F8
+#define REVISION_REG                        0x00FC
+#define OCTETS_TXED_BOTTOM                  0x0100
+#define OCTETS_TXED_TOP                     0x0104
+#define FRAMES_TXED_OK                      0x0108
+#define BROADCAST_TXED                      0x010C
+#define MULTICAST_TXED                      0x0110
+#define PAUSE_FRAMES_TXED                   0x0114
+#define FRAMES_TXED_64                      0x0118
+#define FRAMES_TXED_65                      0x011C
+#define FRAMES_TXED_128                     0x0120
+#define FRAMES_TXED_256                     0x0124
+#define FRAMES_TXED_512                     0x0128
+#define FRAMES_TXED_1024                    0x012C
+#define FRAMES_TXED_1519                    0x0130
+#define TX_UNDERRUNS                        0x0134
+#define SINGLE_COLLISIONS                   0x0138
+#define MULTIPLE_COLLISIONS                 0x013C
+#define EXCESSIVE_COLLISIONS                0x0140
+#define LATE_COLLISIONS                     0x0144
+#define DEFERRED_FRAMES                     0x0148
+#define CRS_ERRORS                          0x014C
+#define OCTETS_RXED_BOTTOM                  0x0150
+#define OCTETS_RXED_TOP                     0x0154
+#define FRAMES_RXED_OK                      0x0158
+#define BROADCAST_RXED                      0x015C
+#define MULTICAST_RXED                      0x0160
+#define PAUSE_FRAMES_RXED                   0x0164
+#define FRAMES_RXED_64                      0x0168
+#define FRAMES_RXED_65                      0x016C
+#define FRAMES_RXED_128                     0x0170
+#define FRAMES_RXED_256                     0x0174
+#define FRAMES_RXED_512                     0x0178
+#define FRAMES_RXED_1024                    0x017C
+#define FRAMES_RXED_1519                    0x0180
+#define UNDERSIZE_FRAMES                    0x0184
+#define EXCESSIVE_RX_LENGTH                 0x0188
+#define RX_JABBERS                          0x018C
+#define FCS_ERRORS                          0x0190
+#define RX_LENGTH_ERRORS                    0x0194
+#define RX_SYMBOL_ERRORS                    0x0198
+#define ALIGNMENT_ERRORS                    0x019C
+#define RX_RESOURCE_ERRORS                  0x01A0
+#define RX_OVERRUNS                         0x01A4
+#define RX_IP_CK_ERRORS                     0x01A8
+#define RX_TCP_CK_ERRORS                    0x01AC
+#define RX_UDP_CK_ERRORS                    0x01B0
+#define AUTO_FLUSHED_PKTS                   0x01B4
+#define TSU_TIMER_INCR_SUB_NSEC             0x01BC
+#define TSU_TIMER_MSB_SEC                   0x01C0
+#define TSU_STROBE_MSB_SEC                  0x01C4
+#define TSU_STROBE_SEC                      0x01C8
+#define TSU_STROBE_NSEC                     0x01CC
+#define TSU_TIMER_SEC                       0x01D0
+#define TSU_TIMER_NSEC                      0x01D4
+#define TSU_TIMER_ADJUST                    0x01D8
+#define TSU_TIMER_INCR                      0x01DC
+#define TSU_PTP_TX_SEC                      0x01E0
+#define TSU_PTP_TX_NSEC                     0x01E4
+#define TSU_PTP_RX_SEC                      0x01E8
+#define TSU_PTP_RX_NSEC                     0x01EC
+#define TSU_PEER_TX_SEC                     0x01F0
+#define TSU_PEER_TX_NSEC                    0x01F4
+#define TSU_PEER_RX_SEC                     0x01F8
+#define TSU_PEER_RX_NSEC                    0x01FC
+#define PCS_CONTROL                         0x0200
+#define PFC_STATUS                          0x026C
+#define RX_LPI                              0x0270
+#define RX_LPI_TIME                         0x0274
+#define TX_LPI                              0x0278
+#define TX_LPI_TIME                         0x027C
+#define DESIGNCFG_DEBUG1                    0x0280
+#define DESIGNCFG_DEBUG2                    0x0284
+#define DESIGNCFG_DEBUG3                    0x0288
+#define DESIGNCFG_DEBUG4                    0x028C
+#define DESIGNCFG_DEBUG5                    0x0290
+#define DESIGNCFG_DEBUG6                    0x0294
+#define DESIGNCFG_DEBUG7                    0x0298
+#define DESIGNCFG_DEBUG8                    0x029C
+#define DESIGNCFG_DEBUG9                    0x02A0
+#define DESIGNCFG_DEBUG10                   0x02A4
+#define DESIGNCFG_DEBUG11                   0x02A8
+#define DESIGNCFG_DEBUG12                   0x02AC
+#define AXI_QOS_CFG_0                       0x02E0
+#define AXI_QOS_CFG_1                       0x02E4
+#define AXI_QOS_CFG_2                       0x02E8
+#define AXI_QOS_CFG_3                       0x02EC
+#define INT_Q1_STATUS                       0x0400
+#define INT_Q2_STATUS                       0x0404
+#define INT_Q3_STATUS                       0x0408
+#define INT_Q4_STATUS                       0x040C
+#define INT_Q5_STATUS                       0x0410
+#define INT_Q6_STATUS                       0x0414
+#define INT_Q7_STATUS                       0x0418
+#define INT_Q8_STATUS                       0x041C
+#define INT_Q9_STATUS                       0x0420
+#define INT_Q10_STATUS                      0x0424
+#define INT_Q11_STATUS                      0x0428
+#define INT_Q12_STATUS                      0x042C
+#define INT_Q13_STATUS                      0x0430
+#define INT_Q14_STATUS                      0x0434
+#define INT_Q15_STATUS                      0x0438
+#define TRANSMIT_Q1_PTR                     0x0440
+#define TRANSMIT_Q2_PTR                     0x0444
+#define TRANSMIT_Q3_PTR                     0x0448
+#define TRANSMIT_Q4_PTR                     0x044C
+#define TRANSMIT_Q5_PTR                     0x0450
+#define TRANSMIT_Q6_PTR                     0x0454
+#define TRANSMIT_Q7_PTR                     0x0458
+#define TRANSMIT_Q8_PTR                     0x045C
+#define TRANSMIT_Q9_PTR                     0x0460
+#define TRANSMIT_Q10_PTR                    0x0464
+#define TRANSMIT_Q11_PTR                    0x0468
+#define TRANSMIT_Q12_PTR                    0x046C
+#define TRANSMIT_Q13_PTR                    0x0470
+#define TRANSMIT_Q14_PTR                    0x0474
+#define TRANSMIT_Q15_PTR                    0x0478
+#define RECEIVE_Q1_PTR                      0x0480
+#define RECEIVE_Q2_PTR                      0x0484
+#define RECEIVE_Q3_PTR                      0x0488
+#define RECEIVE_Q4_PTR                      0x048C
+#define RECEIVE_Q5_PTR                      0x0490
+#define RECEIVE_Q6_PTR                      0x0494
+#define RECEIVE_Q7_PTR                      0x0498
+#define DMA_RXBUF_SIZE_Q1                   0x04A0
+#define DMA_RXBUF_SIZE_Q2                   0x04A4
+#define DMA_RXBUF_SIZE_Q3                   0x04A8
+#define DMA_RXBUF_SIZE_Q4                   0x04AC
+#define DMA_RXBUF_SIZE_Q5                   0x04B0
+#define DMA_RXBUF_SIZE_Q6                   0x04B4
+#define DMA_RXBUF_SIZE_Q7                   0x04B8
+#define CBS_CONTROL                         0x04BC
+#define CBS_IDLESLOPE_Q_A                   0x04C0
+#define CBS_IDLESLOPE_Q_B                   0x04C4
+#define UPPER_TX_Q_BASE_ADDR                0x04C8
+#define TX_BD_CONTROL                       0x04CC
+#define RX_BD_CONTROL                       0x04D0
+#define UPPER_RX_Q_BASE_ADDR                0x04D4
+#define WD_COUNTER                          0x04EC
+#define AXI_TX_FULL_THRESH0                 0x04F8
+#define AXI_TX_FULL_THRESH1                 0x04FC
+#define SCREENING_TYPE_1_REGISTER_0         0x0500
+#define SCREENING_TYPE_1_REGISTER_1         0x0504
+#define SCREENING_TYPE_1_REGISTER_2         0x0508
+#define SCREENING_TYPE_1_REGISTER_3         0x050C
+#define SCREENING_TYPE_1_REGISTER_4         0x0510
+#define SCREENING_TYPE_1_REGISTER_5         0x0514
+#define SCREENING_TYPE_1_REGISTER_6         0x0518
+#define SCREENING_TYPE_1_REGISTER_7         0x051C
+#define SCREENING_TYPE_1_REGISTER_8         0x0520
+#define SCREENING_TYPE_1_REGISTER_9         0x0524
+#define SCREENING_TYPE_1_REGISTER_10        0x0528
+#define SCREENING_TYPE_1_REGISTER_11        0x052C
+#define SCREENING_TYPE_1_REGISTER_12        0x0530
+#define SCREENING_TYPE_1_REGISTER_13        0x0534
+#define SCREENING_TYPE_1_REGISTER_14        0x0538
+#define SCREENING_TYPE_1_REGISTER_15        0x053C
+#define SCREENING_TYPE_2_REGISTER_0         0x0540
+#define SCREENING_TYPE_2_REGISTER_1         0x0544
+#define SCREENING_TYPE_2_REGISTER_2         0x0548
+#define SCREENING_TYPE_2_REGISTER_3         0x054C
+#define SCREENING_TYPE_2_REGISTER_4         0x0550
+#define SCREENING_TYPE_2_REGISTER_5         0x0554
+#define SCREENING_TYPE_2_REGISTER_6         0x0558
+#define SCREENING_TYPE_2_REGISTER_7         0x055C
+#define SCREENING_TYPE_2_REGISTER_8         0x0560
+#define SCREENING_TYPE_2_REGISTER_9         0x0564
+#define SCREENING_TYPE_2_REGISTER_10        0x0568
+#define SCREENING_TYPE_2_REGISTER_11        0x056C
+#define SCREENING_TYPE_2_REGISTER_12        0x0570
+#define SCREENING_TYPE_2_REGISTER_13        0x0574
+#define SCREENING_TYPE_2_REGISTER_14        0x0578
+#define SCREENING_TYPE_2_REGISTER_15        0x057C
+#define TX_SCHED_CTRL                       0x0580
+#define BW_RATE_LIMIT_Q0TO3                 0x0590
+#define BW_RATE_LIMIT_Q4TO7                 0x0594
+#define BW_RATE_LIMIT_Q8TO11                0x0598
+#define BW_RATE_LIMIT_Q12TO15               0x059C
+#define TX_Q_SEG_ALLOC_Q_LOWER              0x05A0
+#define TX_Q_SEG_ALLOC_Q_UPPER              0x05A4
+#define RECEIVE_Q8_PTR                      0x05C0
+#define RECEIVE_Q9_PTR                      0x05C4
+#define RECEIVE_Q10_PTR                     0x05C8
+#define RECEIVE_Q11_PTR                     0x05CC
+#define RECEIVE_Q12_PTR                     0x05D0
+#define RECEIVE_Q13_PTR                     0x05D4
+#define RECEIVE_Q14_PTR                     0x05D8
+#define RECEIVE_Q15_PTR                     0x05DC
+#define DMA_RXBUF_SIZE_Q8                   0x05E0
+#define DMA_RXBUF_SIZE_Q9                   0x05E4
+#define DMA_RXBUF_SIZE_Q10                  0x05E8
+#define DMA_RXBUF_SIZE_Q11                  0x05EC
+#define DMA_RXBUF_SIZE_Q12                  0x05F0
+#define DMA_RXBUF_SIZE_Q13                  0x05F4
+#define DMA_RXBUF_SIZE_Q14                  0x05F8
+#define DMA_RXBUF_SIZE_Q15                  0x05FC
+#define INT_Q1_ENABLE                       0x0600
+#define INT_Q2_ENABLE                       0x0604
+#define INT_Q3_ENABLE                       0x0608
+#define INT_Q4_ENABLE                       0x060C
+#define INT_Q5_ENABLE                       0x0610
+#define INT_Q6_ENABLE                       0x0614
+#define INT_Q7_ENABLE                       0x0618
+#define INT_Q1_DISABLE                      0x0620
+#define INT_Q2_DISABLE                      0x0624
+#define INT_Q3_DISABLE                      0x0628
+#define INT_Q4_DISABLE                      0x062C
+#define INT_Q5_DISABLE                      0x0630
+#define INT_Q6_DISABLE                      0x0634
+#define INT_Q7_DISABLE                      0x0638
+#define INT_Q1_MASK                         0x0640
+#define INT_Q2_MASK                         0x0644
+#define INT_Q3_MASK                         0x0648
+#define INT_Q4_MASK                         0x064C
+#define INT_Q5_MASK                         0x0650
+#define INT_Q6_MASK                         0x0654
+#define INT_Q7_MASK                         0x0658
+#define INT_Q8_ENABLE                       0x0660
+#define INT_Q9_ENABLE                       0x0664
+#define INT_Q10_ENABLE                      0x0668
+#define INT_Q11_ENABLE                      0x066C
+#define INT_Q12_ENABLE                      0x0670
+#define INT_Q13_ENABLE                      0x0674
+#define INT_Q14_ENABLE                      0x0678
+#define INT_Q15_ENABLE                      0x067C
+#define INT_Q8_DISABLE                      0x0680
+#define INT_Q9_DISABLE                      0x0684
+#define INT_Q10_DISABLE                     0x0688
+#define INT_Q11_DISABLE                     0x068C
+#define INT_Q12_DISABLE                     0x0690
+#define INT_Q13_DISABLE                     0x0694
+#define INT_Q14_DISABLE                     0x0698
+#define INT_Q15_DISABLE                     0x069C
+#define INT_Q8_MASK                         0x06A0
+#define INT_Q9_MASK                         0x06A4
+#define INT_Q10_MASK                        0x06A8
+#define INT_Q11_MASK                        0x06AC
+#define INT_Q12_MASK                        0x06B0
+#define INT_Q13_MASK                        0x06B4
+#define INT_Q14_MASK                        0x06B8
+#define INT_Q15_MASK                        0x06BC
+#define SCREENING_TYPE_2_ETHERTYPE_REG_0    0x06E0
+#define SCREENING_TYPE_2_ETHERTYPE_REG_1    0x06E4
+#define SCREENING_TYPE_2_ETHERTYPE_REG_2    0x06E8
+#define SCREENING_TYPE_2_ETHERTYPE_REG_3    0x06EC
+#define SCREENING_TYPE_2_ETHERTYPE_REG_4    0x06F0
+#define SCREENING_TYPE_2_ETHERTYPE_REG_5    0x06F4
+#define SCREENING_TYPE_2_ETHERTYPE_REG_6    0x06F8
+#define SCREENING_TYPE_2_ETHERTYPE_REG_7    0x06FC
+#define TYPE2_COMPARE_0_WORD_0              0x0700
+#define TYPE2_COMPARE_0_WORD_1              0x0704
+#define TYPE2_COMPARE_1_WORD_0              0x0708
+#define TYPE2_COMPARE_1_WORD_1              0x070C
+#define TYPE2_COMPARE_2_WORD_0              0x0710
+#define TYPE2_COMPARE_2_WORD_1              0x0714
+#define TYPE2_COMPARE_3_WORD_0              0x0718
+#define TYPE2_COMPARE_3_WORD_1              0x071C
+#define TYPE2_COMPARE_4_WORD_0              0x0720
+#define TYPE2_COMPARE_4_WORD_1              0x0724
+#define TYPE2_COMPARE_5_WORD_0              0x0728
+#define TYPE2_COMPARE_5_WORD_1              0x072C
+#define TYPE2_COMPARE_6_WORD_0              0x0730
+#define TYPE2_COMPARE_6_WORD_1              0x0734
+#define TYPE2_COMPARE_7_WORD_0              0x0738
+#define TYPE2_COMPARE_7_WORD_1              0x073C
+#define TYPE2_COMPARE_8_WORD_0              0x0740
+#define TYPE2_COMPARE_8_WORD_1              0x0744
+#define TYPE2_COMPARE_9_WORD_0              0x0748
+#define TYPE2_COMPARE_9_WORD_1              0x074C
+#define TYPE2_COMPARE_10_WORD_0             0x0750
+#define TYPE2_COMPARE_10_WORD_1             0x0754
+#define TYPE2_COMPARE_11_WORD_0             0x0758
+#define TYPE2_COMPARE_11_WORD_1             0x075C
+#define TYPE2_COMPARE_12_WORD_0             0x0760
+#define TYPE2_COMPARE_12_WORD_1             0x0764
+#define TYPE2_COMPARE_13_WORD_0             0x0768
+#define TYPE2_COMPARE_13_WORD_1             0x076C
+#define TYPE2_COMPARE_14_WORD_0             0x0770
+#define TYPE2_COMPARE_14_WORD_1             0x0774
+#define TYPE2_COMPARE_15_WORD_0             0x0778
+#define TYPE2_COMPARE_15_WORD_1             0x077C
+#define TYPE2_COMPARE_16_WORD_0             0x0780
+#define TYPE2_COMPARE_16_WORD_1             0x0784
+#define TYPE2_COMPARE_17_WORD_0             0x0788
+#define TYPE2_COMPARE_17_WORD_1             0x078C
+#define TYPE2_COMPARE_18_WORD_0             0x0790
+#define TYPE2_COMPARE_18_WORD_1             0x0794
+#define TYPE2_COMPARE_19_WORD_0             0x0798
+#define TYPE2_COMPARE_19_WORD_1             0x079C
+#define TYPE2_COMPARE_20_WORD_0             0x07A0
+#define TYPE2_COMPARE_20_WORD_1             0x07A4
+#define TYPE2_COMPARE_21_WORD_0             0x07A8
+#define TYPE2_COMPARE_21_WORD_1             0x07AC
+#define TYPE2_COMPARE_22_WORD_0             0x07B0
+#define TYPE2_COMPARE_22_WORD_1             0x07B4
+#define TYPE2_COMPARE_23_WORD_0             0x07B8
+#define TYPE2_COMPARE_23_WORD_1             0x07BC
+#define TYPE2_COMPARE_24_WORD_0             0x07C0
+#define TYPE2_COMPARE_24_WORD_1             0x07C4
+#define TYPE2_COMPARE_25_WORD_0             0x07C8
+#define TYPE2_COMPARE_25_WORD_1             0x07CC
+#define TYPE2_COMPARE_26_WORD_0             0x07D0
+#define TYPE2_COMPARE_26_WORD_1             0x07D4
+#define TYPE2_COMPARE_27_WORD_0             0x07D8
+#define TYPE2_COMPARE_27_WORD_1             0x07DC
+#define TYPE2_COMPARE_28_WORD_0             0x07E0
+#define TYPE2_COMPARE_28_WORD_1             0x07E4
+#define TYPE2_COMPARE_29_WORD_0             0x07E8
+#define TYPE2_COMPARE_29_WORD_1             0x07EC
+#define TYPE2_COMPARE_30_WORD_0             0x07F0
+#define TYPE2_COMPARE_30_WORD_1             0x07F4
+#define TYPE2_COMPARE_31_WORD_0             0x07F8
+#define TYPE2_COMPARE_31_WORD_1             0x07FC
+#define ENST_START_TIME_Q8                  0x0800
+#define ENST_START_TIME_Q9                  0x0804
+#define ENST_START_TIME_Q10                 0x0808
+#define ENST_START_TIME_Q11                 0x080C
+#define ENST_START_TIME_Q12                 0x0810
+#define ENST_START_TIME_Q13                 0x0814
+#define ENST_START_TIME_Q14                 0x0818
+#define ENST_START_TIME_Q15                 0x081C
+#define ENST_ON_TIME_Q8                     0x0820
+#define ENST_ON_TIME_Q9                     0x0824
+#define ENST_ON_TIME_Q10                    0x0828
+#define ENST_ON_TIME_Q11                    0x082C
+#define ENST_ON_TIME_Q12                    0x0830
+#define ENST_ON_TIME_Q13                    0x0834
+#define ENST_ON_TIME_Q14                    0x0838
+#define ENST_ON_TIME_Q15                    0x083C
+#define ENST_OFF_TIME_Q8                    0x0840
+#define ENST_OFF_TIME_Q9                    0x0844
+#define ENST_OFF_TIME_Q10                   0x0848
+#define ENST_OFF_TIME_Q11                   0x084C
+#define ENST_OFF_TIME_Q12                   0x0850
+#define ENST_OFF_TIME_Q13                   0x0854
+#define ENST_OFF_TIME_Q14                   0x0858
+#define ENST_OFF_TIME_Q15                   0x085C
+#define ENST_CONTROL                        0x0880
+#define RX_Q0_FLUSH                         0x0B00
+#define RX_Q1_FLUSH                         0x0B04
+#define RX_Q2_FLUSH                         0x0B08
+#define RX_Q3_FLUSH                         0x0B0C
+#define RX_Q4_FLUSH                         0x0B10
+#define RX_Q5_FLUSH                         0x0B14
+#define RX_Q6_FLUSH                         0x0B18
+#define RX_Q7_FLUSH                         0x0B1C
+#define RX_Q8_FLUSH                         0x0B20
+#define RX_Q9_FLUSH                         0x0B24
+#define RX_Q10_FLUSH                        0x0B28
+#define RX_Q11_FLUSH                        0x0B2C
+#define RX_Q12_FLUSH                        0x0B30
+#define RX_Q13_FLUSH                        0x0B34
+#define RX_Q14_FLUSH                        0x0B38
+#define RX_Q15_FLUSH                        0x0B3C
+#define SCR2_REG0_RATE_LIMIT                0x0B40
+#define SCR2_REG1_RATE_LIMIT                0x0B44
+#define SCR2_REG2_RATE_LIMIT                0x0B48
+#define SCR2_REG3_RATE_LIMIT                0x0B4C
+#define SCR2_REG4_RATE_LIMIT                0x0B50
+#define SCR2_REG5_RATE_LIMIT                0x0B54
+#define SCR2_REG6_RATE_LIMIT                0x0B58
+#define SCR2_REG7_RATE_LIMIT                0x0B5C
+#define SCR2_REG8_RATE_LIMIT                0x0B60
+#define SCR2_REG9_RATE_LIMIT                0x0B64
+#define SCR2_REG10_RATE_LIMIT               0x0B68
+#define SCR2_REG11_RATE_LIMIT               0x0B6C
+#define SCR2_REG12_RATE_LIMIT               0x0B70
+#define SCR2_REG13_RATE_LIMIT               0x0B74
+#define SCR2_REG14_RATE_LIMIT               0x0B78
+#define SCR2_REG15_RATE_LIMIT               0x0B7C
+#define SCR2_RATE_STATUS                    0x0B80
+#define ASF_INT_STATUS                      0x0E00
+#define ASF_INT_RAW_STATUS                  0x0E04
+#define ASF_INT_MASK                        0x0E08
+#define ASF_INT_TEST                        0x0E0C
+#define ASF_FATAL_NONFATAL_SELECT           0x0E10
+#define ASF_TRANS_TO_FAULT_MASK             0x0E34
+#define ASF_TRANS_TO_FAULT_STATUS           0x0E38
+#define ASF_PROTOCOL_FAULT_MASK             0x0E40
+#define ASF_PROTOCOL_FAULT_STATUS           0x0E44
+
+/* Register bit field definitions *******************************************/
+
+/* NETWORK_CONTROL:
+ * The network control register contains general MAC control functions
+ * for both receiver and transmitter.
+ */
+
+#define NETWORK_CONTROL_LOOPBACK                                (1 << 0)
+#define NETWORK_CONTROL_LOOPBACK_LOCAL                          (1 << 1)
+#define NETWORK_CONTROL_ENABLE_RECEIVE                          (1 << 2)
+#define NETWORK_CONTROL_ENABLE_TRANSMIT                         (1 << 3)
+#define NETWORK_CONTROL_MAN_PORT_EN                             (1 << 4)
+#define NETWORK_CONTROL_CLEAR_ALL_STATS_REGS                    (1 << 5)
+#define NETWORK_CONTROL_INC_ALL_STATS_REGS                      (1 << 6)
+#define NETWORK_CONTROL_STATS_WRITE_EN                          (1 << 7)
+#define NETWORK_CONTROL_BACK_PRESSURE                           (1 << 8)
+#define NETWORK_CONTROL_TRANSMIT_START                          (1 << 9)
+#define NETWORK_CONTROL_TRANSMIT_HALT                           (1 << 10)
+#define NETWORK_CONTROL_TX_PAUSE_FRAME_REQ                      (1 << 11)
+#define NETWORK_CONTROL_TX_PAUSE_FRAME_ZERO                     (1 << 12)
+#define NETWORK_CONTROL_STORE_RX_TS                             (1 << 15)
+#define NETWORK_CONTROL_PFC_ENABLE                              (1 << 16)
+#define NETWORK_CONTROL_TRANSMIT_PFC_PRIORITY_BASED_PAUSE_FRAME (1 << 17)
+#define NETWORK_CONTROL_FLUSH_RX_PKT_PCLK                       (1 << 18)
+#define NETWORK_CONTROL_TX_LPI_EN                               (1 << 19)
+#define NETWORK_CONTROL_PTP_UNICAST_ENA                         (1 << 20)
+#define NETWORK_CONTROL_ALT_SGMII_MODE                          (1 << 21)
+#define NETWORK_CONTROL_STORE_UDP_OFFSET                        (1 << 22)
+#define NETWORK_CONTROL_EXT_TSU_PORT_ENABLE                     (1 << 23)
+#define NETWORK_CONTROL_ONE_STEP_SYNC_MODE                      (1 << 24)
+#define NETWORK_CONTROL_PFC_CTRL                                (1 << 25)
+#define NETWORK_CONTROL_EXT_RXQ_SEL_EN                          (1 << 26)
+#define NETWORK_CONTROL_OSS_CORRECTION_FIELD                    (1 << 27)
+#define NETWORK_CONTROL_SEL_MII_ON_RGMII                        (1 << 28)
+#define NETWORK_CONTROL_TWO_PT_FIVE_GIG                         (1 << 29)
+#define NETWORK_CONTROL_IFG_EATS_QAV_CREDIT                     (1 << 30)
+
+/* NETWORK_CONFIG:
+ * The network configuration register contains functions for
+ * setting the mode of operation for the Gigabit Ethernet MAC.
+ */
+
+#define NETWORK_CONFIG_SPEED                                    (1 << 0)
+#define NETWORK_CONFIG_FULL_DUPLEX                              (1 << 1)
+#define NETWORK_CONFIG_DISCARD_NON_VLAN_FRAMES                  (1 << 2)
+#define NETWORK_CONFIG_JUMBO_FRAMES                             (1 << 3)
+#define NETWORK_CONFIG_COPY_ALL_FRAMES                          (1 << 4)
+#define NETWORK_CONFIG_NO_BROADCAST                             (1 << 5)
+#define NETWORK_CONFIG_MULTICAST_HASH_ENABLE                    (1 << 6)
+#define NETWORK_CONFIG_UNICAST_HASH_ENABLE                      (1 << 7)
+#define NETWORK_CONFIG_RECEIVE_1536_BYTE_FRAMES                 (1 << 8)
+#define NETWORK_CONFIG_EXTERNAL_ADDRESS_MATCH_ENABLE            (1 << 9)
+#define NETWORK_CONFIG_GIGABIT_MODE_ENABLE                      (1 << 10)
+#define NETWORK_CONFIG_PCS_SELECT                               (1 << 11)
+#define NETWORK_CONFIG_RETRY_TEST                               (1 << 12)
+#define NETWORK_CONFIG_PAUSE_ENABLE                             (1 << 13)
+#define NETWORK_CONFIG_RECEIVE_BUFFER_OFFSET_MASK               (3)
+#define NETWORK_CONFIG_RECEIVE_BUFFER_OFFSET_SHIFT              (14)
+#define NETWORK_CONFIG_LENGTH_FIELD_ERROR_FRAME_DISCARD         (1 << 16)
+#define NETWORK_CONFIG_FCS_REMOVE                               (1 << 17)
+#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT                  (18)
+#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK                   (7)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8                    (0 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16                   (1 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32                   (2 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48                   (3 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64                   (4 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96                   (5 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT                     (21)
+#define NETWORK_CONFIG_DATA_BUS_WIDTH_MASK                      (3)
+#define NETWORK_CONFIG_DISABLE_COPY_OF_PAUSE_FRAMES             (1 << 23)
+#define NETWORK_CONFIG_RECEIVE_CHECKSUM_OFFLOAD_ENABLE          (1 << 24)
+#define NETWORK_CONFIG_EN_HALF_DUPLEX_RX                        (1 << 25)
+#define NETWORK_CONFIG_IGNORE_RX_FCS                            (1 << 26)
+#define NETWORK_CONFIG_SGMII_MODE_ENABLE                        (1 << 27)
+#define NETWORK_CONFIG_IPG_STRETCH_ENABLE                       (1 << 28)
+#define NETWORK_CONFIG_NSP_CHANGE                               (1 << 29)
+#define NETWORK_CONFIG_IGNORE_IPG_RX_ER                         (1 << 30)
+#define NETWORK_CONFIG_UNI_DIRECTION_ENABLE                     (1 << 31)
+
+/* NETWORK_STATUS:
+ * The network status register returns status information with respect
+ * to the PHY management MDIO interface, the PCS, priority flow control,
+ * LPI and other status.
+ */
+
+#define NETWORK_STATUS_PCS_LINK_STATE                           (1 << 0)
+#define NETWORK_STATUS_MDIO_IN                                  (1 << 1)
+#define NETWORK_STATUS_MAN_DONE                                 (1 << 2)
+#define NETWORK_STATUS_MAC_FULL_DUPLEX                          (1 << 3)
+#define NETWORK_STATUS_MAC_PAUSE_RX_EN                          (1 << 4)
+#define NETWORK_STATUS_MAC_PAUSE_TX_EN                          (1 << 5)
+#define NETWORK_STATUS_PFC_NEGOTIATE_PCLK                       (1 << 6)
+#define NETWORK_STATUS_LPI_INDICATE_PCLK                        (1 << 7)
+#define NETWORK_STATUS_AXI_XACTION_OUTSTANDING                  (1 << 8)
+#define NETWORK_STATUS_LINK_FAULT_INDICATION_SHIFT              (1 << 9)
+#define NETWORK_STATUS_LINK_FAULT_INDICATION_MASK               (7)
+
+/* DMA_CONFIG:
+ * DMA Configuration Register
+ */
+
+#define DMA_CONFIG_AMBA_BURST_LENGTH_SHIFT          (0)       /* Bits: 0-4 */
+#define DMA_CONFIG_AMBA_BURST_LENGTH_MASK           (0x1f)
+#define DMA_CONFIG_HDR_DATA_SPLITTING_EN            (1 << 5)  /* Bit 5 */
+#define DMA_CONFIG_ENDIAN_SWAP_MANAGEMENT           (1 << 6)  /* Bit 6 */
+#define DMA_CONFIG_ENDIAN_SWAP_PACKET               (1 << 7)  /* Bit 7 */
+#define DMA_CONFIG_RX_PBUF_SIZE_SHIFT               (8)       /* Bits: 8-9 */
+#define DMA_CONFIG_RX_PBUF_SIZE_MASK                (3)
+#define DMA_CONFIG_TX_PBUF_SIZE                     (1 << 10) /* Bit 10 */
+#define DMA_CONFIG_TX_PBUF_TCP_EN                   (1 << 11) /* Bit 11 */
+#define DMA_CONFIG_INFINITE_LAST_DBUF_SIZE_EN       (1 << 12) /* Bit 12 */
+#define DMA_CONFIG_CRC_ERROR_REPORT                 (1 << 13) /* Bit 13 */
+#define DMA_CONFIG_RX_BUF_SIZE_MASK                 (0xff)
+#define DMA_CONFIG_RX_BUF_SIZE_SHIFT                (16)
+#define DMA_CONFIG_FORCE_DISCARD_ON_ERR             (1 << 24) /* Bit 24 */
+#define DMA_CONFIG_FORCE_MAX_AMBA_BURST_RX          (1 << 25) /* Bit 25 */
+#define DMA_CONFIG_FORCE_MAX_AMBA_BURST_TX          (1 << 26) /* Bit 26 */
+                                                              /* Bit 27 reserved */
+#define DMA_CONFIG_RX_BD_EXTENDED_MODE_EN           (1 << 28) /* Bit 28 */
+#define DMA_CONFIG_TX_BD_EXTENDED_MODE_EN           (1 << 29) /* Bit 29 */
+#define DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1             (1 << 30) /* Bit 30 */
+                                                              /* Bit 31 reserved */
+
+/* TRANSMIT_STATUS:
+ * This register, when read, provides details of the status
+ * of the transmit path. Once read, individual bits may be cleared
+ * by writing 1 to them. It is not possible to set a bit to 1 by writing
+ * to the register.
+ */
+
+#define TRANSMIT_STATUS_USED_BIT_READ               (1 << 0)
+#define TRANSMIT_STATUS_COLLISION_OCCURED           (1 << 1)
+#define TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED        (1 << 2)
+#define TRANSMIT_STATUS_TRANSMIT_GO                 (1 << 3)
+#define TRANSMIT_STATUS_AMBA_ERROR                  (1 << 4)
+#define TRANSMIT_STATUS_TRANSMIT_COMPLETE           (1 << 5)
+#define TRANSMIT_STATUS_UNDERRUN                    (1 << 6)
+#define TRANSMIT_STATUS_LATE_COLLISION              (1 << 7)
+#define TRANSMIT_STATUS_RESP_NOT_OK                 (1 << 8)
+#define TRANSMIT_STATUS_TX_MAC_LOCKUP               (1 << 9)
+#define TRANSMIT_STATUS_TX_DMA_LOCKUP               (1 << 10)
+
+/* RECEIVE_STATUS:
+ * This register, when read, provides details of the status
+ * of the receive path. Once read, individual bits may be cleared
+ * by writing 1 to them. It is not possible to set a bit to 1 by writing
+ * to the register.
+ */
+#define RECEIVE_STATUS_BUFFER_NOT_AVAILABLE         (1 << 0)
+#define RECEIVE_STATUS_FRAME_RECEIVED               (1 << 1)
+#define RECEIVE_STATUS_RECEIVE_OVERRUN              (1 << 2)
+#define RECEIVE_STATUS_RESP_NOT_OK                  (1 << 3)
+#define RECEIVE_STATUS_RX_MAC_LOCKUP                (1 << 4)
+#define RECEIVE_STATUS_RX_DMA_LOCKUP                (1 << 5)
+
+/* PHY_MANAGEMENT:
+ */
+
+#define PHY_MANAGEMENT_PHY_DATA_SHIFT               (0)
+#define PHY_MANAGEMENT_PHY_DATA_MASK                (0xffff)
+#define PHY_MANAGEMENT_PHY_DATA(n)                  ((n & PHY_MANAGEMENT_PHY_DATA_MASK) << PHY_MANAGEMENT_PHY_DATA_SHIFT)
+#define PHY_MANAGEMENT_WRITE10                      (2 << PHY_MANAGEMENT_WRITE10_SHIFT)
+#define PHY_MANAGEMENT_WRITE10_SHIFT                (16)
+#define PHY_MANAGEMENT_WRITE10_MASK                 (3)
+#define PHY_MANAGEMENT_REG_ADDRESS_SHIFT            (18)
+#define PHY_MANAGEMENT_REG_ADDRESS_MASK             (0x1f)
+#define PHY_MANAGEMENT_REG_ADDRESS(n)               ((n & PHY_MANAGEMENT_REG_ADDRESS_MASK ) << PHY_MANAGEMENT_REG_ADDRESS_SHIFT)
+#define PHY_MANAGEMENT_PHY_ADDRESS_SHIFT            (23)
+#define PHY_MANAGEMENT_PHY_ADDRESS_MASK             (0x1f)
+#define PHY_MANAGEMENT_PHY_ADDRESS(n)               ((n & PHY_MANAGEMENT_PHY_ADDRESS_MASK ) << PHY_MANAGEMENT_PHY_ADDRESS_SHIFT)
+#define PHY_MANAGEMENT_OPERATION_SHIFT              (28)
+#define PHY_MANAGEMENT_OPERATION_MASK               (3)
+#  define PHY_MANAGEMENT_OPERATION_READ             (2 << PHY_MANAGEMENT_OPERATION_SHIFT)
+#  define PHY_MANAGEMENT_OPERATION_WRITE            (1 << PHY_MANAGEMENT_OPERATION_SHIFT)
+#define PHY_MANAGEMENT_WRITE_1			                (1 << 30)
+#define PHY_MANAGEMENT_WRITE_0			                (1 << 31)

Review comment:
       ```suggestion
   #define PHY_MANAGEMENT_WRITE_1			          (1 << 30)
   #define PHY_MANAGEMENT_WRITE_0			          (1 << 31)
   ```

##########
File path: arch/risc-v/src/mpfs/Kconfig
##########
@@ -317,10 +337,151 @@ config MPFS_DMA
 	---help---
 		Enable DMA Support. MPFS DMA is Memory-to-Memory only.
 
-menu "MPFS Others"
+menu "Ethernet MAC configuration"
+	depends on MPFS_ETHMAC
+
+choice
+	prompt "MII Interface mode"
+
+config MPFS_MAC_SGMII
+	bool "SGMII"
+	---help---
+		Use Ethernet SGMII interface.
+
+config MPFS_MAC_GMII
+	bool "GMII"
+	---help---
+		Use Ethernet GMII interface.
+endchoice
+
+config MPFS_PHYADDR
+	int "PHY address"
+	default 1
+	---help---
+		The 5-bit address of the PHY on the board.  Default: 1
+
+config MPFS_MAC_NO_BROADCAST
+	bool "Disable Broadcast"
+	default n
+	---help---
+		Select to disable receipt of broadcast packets.
+
+config MPFS_MAC_AUTONEG
+	bool "Use autonegotiation"
+	default y
+	---help---
+		Use PHY autonegotiation to determine speed and mode
+
+config MPFS_MAC_DISABLE_1000MBPS
+	bool "Disable Gigabit mode"
+	default n
+	depends on MPFS_MAC_AUTONEG
+	---help---
+		Select to disable Gigabit speed support.
+		If disabled then autonegotiation don't advertise 1GB mode
+
+config MPFS_PHYINIT
+	bool "Use board phyinit"
+	default n
+	---help---
+		call mpfs_phy_boardinitialize() on init
+
+if !MPFS_MAC_AUTONEG
+
+config MPFS_MAC_ETHFD
+	bool "Full duplex"
+	default n
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to
+		select full duplex mode. Default: half-duplex
+
+choice
+	prompt "MAC Speed"
+	default MPFS_MAC_ETH100MBPS
+	---help---
+		If autonegotiation is not used, then you must select the fixed speed
+		of the PHY
+
+config MPFS_MAC_ETH10MBPS
+	bool "10 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 10 MBps
+		speed.  Default: 100 Mbps
+
+config MPFS_MAC_ETH100MBPS
+	bool "100 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 100 MBps
+		speed.  Default: 100 Mbps
+
+config MPFS_MAC_ETH1000MBPS
+	bool "1000 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 1000 MBps
+		speed.  Default: 100 Mbps
+
+endchoice # GMAC speed
+
+endif # !MPFS_MAC_AUTONEG
+
+config MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ
+	int "MDC Clock Source (Hz)"
+	default 125000000
+	range 1000000 240000000
+	depends on MPFS_ETHMAC
+	---help---
+		Select the MDC clock source frequency.
+
+config MPFS_ETHMAC_NTXBUFFERS
+	int "Number of TX DMA Buffers"
+	default 8
+	depends on MPFS_ETHMAC
+	---help---
+		Select the number of TX DMA buffers.
+
+config MPFS_ETHMAC_NRXBUFFERS
+	int "Number of RX DMA Buffers"
+	default 16
+	depends on MPFS_ETHMAC
+	---help---
+		Select the number of RX DMA buffers.
+
+config MPFS_ETHMAC_64BIT_ADDRESS_MODE
+	bool "64-bit DMA address mode"
+	default n
+	depends on MPFS_ETHMAC
+	---help---
+		Select to enable 64-bit DMA address mode. This is only needed if the
+		Nuttx memory address is above 4GB.
+
+config MPFS_ETHMAC_REGDEBUG
+	bool "Register-Level Debug"
+	default n
+	depends on DEBUG_NET_INFO
+	---help---
+		Enable very low-level register access debug.  Depends on
+		CONFIG_DEBUG_FEATURES.
+
+config MPFS_MPU_DMA_ENABLE
+	bool "Enable ALL memory access for eth DMA"
+	default n
+	---help---
+		Set ALL memory accessible for eth DMA.
+		This should be done on bootloader. This is mostly for testing only.
 
 endmenu
 
+

Review comment:
       ```suggestion
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN       (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS+1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET		    (1 << 15)
+#  define CORE_RMII_LOOPBACK	  (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX	(1 << 1)
+#  define CORE_RMII_100MBIT	    (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1*CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60*CLK_TCK)

Review comment:
       ```suggestion
   #define MPFS_TXTIMEOUT   (60 * CLK_TCK)
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN       (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS+1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET		    (1 << 15)
+#  define CORE_RMII_LOOPBACK	  (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX	(1 << 1)
+#  define CORE_RMII_100MBIT	    (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1*CLK_TCK)

Review comment:
       ```suggestion
   #define MPFS_WDDELAY     (1 * CLK_TCK)
   ```

##########
File path: arch/risc-v/src/mpfs/Kconfig
##########
@@ -317,10 +337,151 @@ config MPFS_DMA
 	---help---
 		Enable DMA Support. MPFS DMA is Memory-to-Memory only.
 
-menu "MPFS Others"
+menu "Ethernet MAC configuration"
+	depends on MPFS_ETHMAC
+
+choice
+	prompt "MII Interface mode"
+
+config MPFS_MAC_SGMII
+	bool "SGMII"
+	---help---
+		Use Ethernet SGMII interface.
+
+config MPFS_MAC_GMII
+	bool "GMII"
+	---help---
+		Use Ethernet GMII interface.
+endchoice
+
+config MPFS_PHYADDR
+	int "PHY address"
+	default 1
+	---help---
+		The 5-bit address of the PHY on the board.  Default: 1
+
+config MPFS_MAC_NO_BROADCAST
+	bool "Disable Broadcast"
+	default n
+	---help---
+		Select to disable receipt of broadcast packets.
+
+config MPFS_MAC_AUTONEG
+	bool "Use autonegotiation"
+	default y
+	---help---
+		Use PHY autonegotiation to determine speed and mode
+
+config MPFS_MAC_DISABLE_1000MBPS
+	bool "Disable Gigabit mode"
+	default n
+	depends on MPFS_MAC_AUTONEG
+	---help---
+		Select to disable Gigabit speed support.
+		If disabled then autonegotiation don't advertise 1GB mode
+
+config MPFS_PHYINIT
+	bool "Use board phyinit"
+	default n
+	---help---
+		call mpfs_phy_boardinitialize() on init
+
+if !MPFS_MAC_AUTONEG
+
+config MPFS_MAC_ETHFD
+	bool "Full duplex"
+	default n
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to
+		select full duplex mode. Default: half-duplex
+
+choice
+	prompt "MAC Speed"
+	default MPFS_MAC_ETH100MBPS
+	---help---
+		If autonegotiation is not used, then you must select the fixed speed
+		of the PHY
+
+config MPFS_MAC_ETH10MBPS
+	bool "10 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 10 MBps
+		speed.  Default: 100 Mbps
+
+config MPFS_MAC_ETH100MBPS
+	bool "100 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 100 MBps
+		speed.  Default: 100 Mbps
+
+config MPFS_MAC_ETH1000MBPS
+	bool "1000 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 1000 MBps
+		speed.  Default: 100 Mbps
+
+endchoice # GMAC speed
+
+endif # !MPFS_MAC_AUTONEG
+
+config MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ
+	int "MDC Clock Source (Hz)"
+	default 125000000
+	range 1000000 240000000
+	depends on MPFS_ETHMAC
+	---help---
+		Select the MDC clock source frequency.
+
+config MPFS_ETHMAC_NTXBUFFERS
+	int "Number of TX DMA Buffers"
+	default 8
+	depends on MPFS_ETHMAC
+	---help---
+		Select the number of TX DMA buffers.
+
+config MPFS_ETHMAC_NRXBUFFERS
+	int "Number of RX DMA Buffers"
+	default 16
+	depends on MPFS_ETHMAC
+	---help---
+		Select the number of RX DMA buffers.
+
+config MPFS_ETHMAC_64BIT_ADDRESS_MODE
+	bool "64-bit DMA address mode"
+	default n
+	depends on MPFS_ETHMAC
+	---help---
+		Select to enable 64-bit DMA address mode. This is only needed if the
+		Nuttx memory address is above 4GB.
+
+config MPFS_ETHMAC_REGDEBUG
+	bool "Register-Level Debug"
+	default n
+	depends on DEBUG_NET_INFO
+	---help---
+		Enable very low-level register access debug.  Depends on
+		CONFIG_DEBUG_FEATURES.
+
+config MPFS_MPU_DMA_ENABLE
+	bool "Enable ALL memory access for eth DMA"
+	default n
+	---help---
+		Set ALL memory accessible for eth DMA.
+		This should be done on bootloader. This is mostly for testing only.
 
 endmenu
 
+
+config MPFS_HAVE_CORERMII
+	bool "CoreRMII FPGA IP block configured"
+	default n
+
+config MPFS_CORERMII_ADDRESS
+	int "CoreRMII Phy address"
+	default 1
+	depends on MPFS_HAVE_CORERMII
+
+

Review comment:
       ```suggestion
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jlaitine commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jlaitine commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r826970230



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3855 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == (void *)MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == (void *)MPFS_GEM1_LO_BASE)

Review comment:
       No need to cast to void *, comparing uintptr_t
   ```suggestion
     if (priv->regbase == MPFS_GEM1_LO_BASE)
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r826071307



##########
File path: arch/risc-v/src/mpfs/Kconfig
##########
@@ -317,10 +337,151 @@ config MPFS_DMA
 	---help---
 		Enable DMA Support. MPFS DMA is Memory-to-Memory only.
 
-menu "MPFS Others"
+menu "Ethernet MAC configuration"
+	depends on MPFS_ETHMAC
+
+choice
+	prompt "MII Interface mode"
+
+config MPFS_MAC_SGMII
+	bool "SGMII"
+	---help---
+		Use Ethernet SGMII interface.
+
+config MPFS_MAC_GMII
+	bool "GMII"
+	---help---
+		Use Ethernet GMII interface.
+endchoice
+
+config MPFS_PHYADDR
+	int "PHY address"
+	default 1
+	---help---
+		The 5-bit address of the PHY on the board.  Default: 1
+
+config MPFS_MAC_NO_BROADCAST
+	bool "Disable Broadcast"
+	default n
+	---help---
+		Select to disable receipt of broadcast packets.
+
+config MPFS_MAC_AUTONEG
+	bool "Use autonegotiation"
+	default y
+	---help---
+		Use PHY autonegotiation to determine speed and mode
+
+config MPFS_MAC_DISABLE_1000MBPS
+	bool "Disable Gigabit mode"
+	default n
+	depends on MPFS_MAC_AUTONEG
+	---help---
+		Select to disable Gigabit speed support.
+		If disabled then autonegotiation don't advertise 1GB mode
+
+config MPFS_PHYINIT
+	bool "Use board phyinit"
+	default n
+	---help---
+		call mpfs_phy_boardinitialize() on init
+
+if !MPFS_MAC_AUTONEG
+
+config MPFS_MAC_ETHFD
+	bool "Full duplex"
+	default n
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to
+		select full duplex mode. Default: half-duplex
+
+choice
+	prompt "MAC Speed"
+	default MPFS_MAC_ETH100MBPS
+	---help---
+		If autonegotiation is not used, then you must select the fixed speed
+		of the PHY
+
+config MPFS_MAC_ETH10MBPS
+	bool "10 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 10 MBps
+		speed.  Default: 100 Mbps
+
+config MPFS_MAC_ETH100MBPS
+	bool "100 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 100 MBps
+		speed.  Default: 100 Mbps
+
+config MPFS_MAC_ETH1000MBPS
+	bool "1000 Mbps"
+	---help---

Review comment:
       The second one make more sense.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#issuecomment-1069406948


   following configs needs update:
   - esp32c3-devkit/ble
   - bl602evb/wifi
   
   need to add: `CONFIG_NETDEV_LATEINIT` for those to disable call to riscv_netinitialize() like on arm world.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829871229



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3823 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;    /* Buffer address */
+    uint32_t status;  /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi; /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;    /* Buffer address */
+    uint32_t status;  /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi; /* Buffer address high 32-bits */
+    uint32_t unused;  /*  */

Review comment:
       2 spaces indentation.

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3837 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t              *rxbuffer;        /* Allocated RX buffers */
+        uint8_t              *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t             txhead;           /* Circular buffer head index */
+        uint32_t             txtail;           /* Circular buffer tail index */
+        uint32_t             rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t    *int_status;      /* interrupt status */
+        volatile uint32_t    *int_mask;        /* interrupt mask */
+        volatile uint32_t    *int_enable;      /* interrupt enable */
+        volatile uint32_t    *int_disable;     /* interrupt disable */
+        volatile uint32_t    *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t    *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t    *dma_rxbuf_size;  /* RX queue buffer size */

Review comment:
       Please address




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jlaitine commented on pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jlaitine commented on pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#issuecomment-1072306779


   Tested also on aries m100pfs , seems to be working great


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] acassis commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
acassis commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r825888090



##########
File path: arch/risc-v/src/mpfs/Kconfig
##########
@@ -317,10 +337,151 @@ config MPFS_DMA
 	---help---
 		Enable DMA Support. MPFS DMA is Memory-to-Memory only.
 
-menu "MPFS Others"
+menu "Ethernet MAC configuration"
+	depends on MPFS_ETHMAC
+
+choice
+	prompt "MII Interface mode"
+
+config MPFS_MAC_SGMII
+	bool "SGMII"
+	---help---
+		Use Ethernet SGMII interface.
+
+config MPFS_MAC_GMII
+	bool "GMII"
+	---help---
+		Use Ethernet GMII interface.
+endchoice
+
+config MPFS_PHYADDR
+	int "PHY address"
+	default 1
+	---help---
+		The 5-bit address of the PHY on the board.  Default: 1
+
+config MPFS_MAC_NO_BROADCAST
+	bool "Disable Broadcast"
+	default n
+	---help---
+		Select to disable receipt of broadcast packets.
+
+config MPFS_MAC_AUTONEG
+	bool "Use autonegotiation"
+	default y
+	---help---
+		Use PHY autonegotiation to determine speed and mode
+
+config MPFS_MAC_DISABLE_1000MBPS
+	bool "Disable Gigabit mode"
+	default n
+	depends on MPFS_MAC_AUTONEG
+	---help---
+		Select to disable Gigabit speed support.
+		If disabled then autonegotiation don't advertise 1GB mode
+
+config MPFS_PHYINIT
+	bool "Use board phyinit"
+	default n
+	---help---
+		call mpfs_phy_boardinitialize() on init
+
+if !MPFS_MAC_AUTONEG
+
+config MPFS_MAC_ETHFD
+	bool "Full duplex"
+	default n
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to
+		select full duplex mode. Default: half-duplex
+
+choice
+	prompt "MAC Speed"
+	default MPFS_MAC_ETH100MBPS
+	---help---
+		If autonegotiation is not used, then you must select the fixed speed
+		of the PHY
+
+config MPFS_MAC_ETH10MBPS
+	bool "10 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 10 MBps
+		speed.  Default: 100 Mbps
+
+config MPFS_MAC_ETH100MBPS
+	bool "100 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 100 MBps
+		speed.  Default: 100 Mbps
+
+config MPFS_MAC_ETH1000MBPS
+	bool "1000 Mbps"
+	---help---
+		If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 1000 MBps
+		speed.  Default: 100 Mbps
+
+endchoice # GMAC speed
+
+endif # !MPFS_MAC_AUTONEG
+
+config MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ
+	int "MDC Clock Source (Hz)"
+	default 125000000
+	range 1000000 240000000
+	depends on MPFS_ETHMAC
+	---help---
+		Select the MDC clock source frequency.
+
+config MPFS_ETHMAC_NTXBUFFERS
+	int "Number of TX DMA Buffers"
+	default 8
+	depends on MPFS_ETHMAC
+	---help---
+		Select the number of TX DMA buffers.
+
+config MPFS_ETHMAC_NRXBUFFERS
+	int "Number of RX DMA Buffers"
+	default 16
+	depends on MPFS_ETHMAC
+	---help---
+		Select the number of RX DMA buffers.
+
+config MPFS_ETHMAC_64BIT_ADDRESS_MODE
+	bool "64-bit DMA address mode"
+	default n
+	depends on MPFS_ETHMAC
+	---help---
+		Select to enable 64-bit DMA address mode. This is only needed if the
+		Nuttx memory address is above 4GB.
+
+config MPFS_ETHMAC_REGDEBUG
+	bool "Register-Level Debug"
+	default n
+	depends on DEBUG_NET_INFO
+	---help---
+		Enable very low-level register access debug.  Depends on
+		CONFIG_DEBUG_FEATURES.
+
+config MPFS_MPU_DMA_ENABLE
+	bool "Enable ALL memory access for eth DMA"

Review comment:
       ```suggestion
   	bool "Enable ALL memory access for Ethernet DMA"
   ```

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN       (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS+1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET		    (1 << 15)
+#  define CORE_RMII_LOOPBACK	  (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX	(1 << 1)
+#  define CORE_RMII_100MBIT	    (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1*CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60*CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *
+ *

Review comment:
       ditto

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3860 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN       (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS+1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET		    (1 << 15)
+#  define CORE_RMII_LOOPBACK	  (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX	(1 << 1)
+#  define CORE_RMII_100MBIT	    (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1*CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60*CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *
+ *

Review comment:
       Please include the description text and remove extra line




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r828169342



##########
File path: arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h
##########
@@ -0,0 +1,701 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H
+#define __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/mpfs_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets *********************************************************/
+
+#define NETWORK_CONTROL                     0x0000
+#define NETWORK_CONFIG                      0x0004
+#define NETWORK_STATUS                      0x0008
+#define DMA_CONFIG                          0x0010
+#define TRANSMIT_STATUS                     0x0014
+#define RECEIVE_Q_PTR                       0x0018
+#define TRANSMIT_Q_PTR                      0x001C
+#define RECEIVE_STATUS                      0x0020
+#define INT_STATUS                          0x0024
+#define INT_ENABLE                          0x0028
+#define INT_DISABLE                         0x002C
+#define INT_MASK                            0x0030
+#define PHY_MANAGEMENT                      0x0034
+#define PAUSE_TIME                          0x0038
+#define TX_PAUSE_QUANTUM                    0x003C
+#define PBUF_TXCUTTHRU                      0x0040
+#define PBUF_RXCUTTHRU                      0x0044
+#define JUMBO_MAX_LENGTH                    0x0048
+#define AXI_MAX_PIPELINE                    0x0054
+#define RSC_CONTROL                         0x0058
+#define INT_MODERATION                      0x005C
+#define SYS_WAKE_TIME                       0x0060
+#define LOCKUP_CONFIG                       0x0068
+#define MAC_LOCKUP_TIME                     0x006C
+#define LOCKUP_CONFIG3                      0x0070
+#define RX_WATER_MARK                       0x007C
+#define HASH_BOTTOM                         0x0080
+#define HASH_TOP                            0x0084
+#define SPEC_ADD1_BOTTOM                    0x0088
+#define SPEC_ADD1_TOP                       0x008C
+#define SPEC_ADD2_BOTTOM                    0x0090
+#define SPEC_ADD2_TOP                       0x0094
+#define SPEC_ADD3_BOTTOM                    0x0098
+#define SPEC_ADD3_TOP                       0x009C
+#define SPEC_ADD4_BOTTOM                    0x00A0
+#define SPEC_ADD4_TOP                       0x00A4
+#define SPEC_TYPE1                          0x00A8
+#define SPEC_TYPE2                          0x00AC
+#define SPEC_TYPE3                          0x00B0
+#define SPEC_TYPE4                          0x00B4
+#define WOL_REGISTER                        0x00B8
+#define STRETCH_RATIO                       0x00BC
+#define STACKED_VLAN                        0x00C0
+#define TX_PFC_PAUSE                        0x00C4
+#define MASK_ADD1_BOTTOM                    0x00C8
+#define MASK_ADD1_TOP                       0x00CC
+#define DMA_ADDR_OR_MASK                    0x00D0
+#define RX_PTP_UNICAST                      0x00D4
+#define TX_PTP_UNICAST                      0x00D8
+#define TSU_NSEC_CMP                        0x00DC
+#define TSU_SEC_CMP                         0x00E0
+#define TSU_MSB_SEC_CMP                     0x00E4
+#define TSU_PTP_TX_MSB_SEC                  0x00E8
+#define TSU_PTP_RX_MSB_SEC                  0x00EC
+#define TSU_PEER_TX_MSB_SEC                 0x00F0
+#define TSU_PEER_RX_MSB_SEC                 0x00F4
+#define DPRAM_FILL_DBG                      0x00F8
+#define REVISION_REG                        0x00FC
+#define OCTETS_TXED_BOTTOM                  0x0100
+#define OCTETS_TXED_TOP                     0x0104
+#define FRAMES_TXED_OK                      0x0108
+#define BROADCAST_TXED                      0x010C
+#define MULTICAST_TXED                      0x0110
+#define PAUSE_FRAMES_TXED                   0x0114
+#define FRAMES_TXED_64                      0x0118
+#define FRAMES_TXED_65                      0x011C
+#define FRAMES_TXED_128                     0x0120
+#define FRAMES_TXED_256                     0x0124
+#define FRAMES_TXED_512                     0x0128
+#define FRAMES_TXED_1024                    0x012C
+#define FRAMES_TXED_1519                    0x0130
+#define TX_UNDERRUNS                        0x0134
+#define SINGLE_COLLISIONS                   0x0138
+#define MULTIPLE_COLLISIONS                 0x013C
+#define EXCESSIVE_COLLISIONS                0x0140
+#define LATE_COLLISIONS                     0x0144
+#define DEFERRED_FRAMES                     0x0148
+#define CRS_ERRORS                          0x014C
+#define OCTETS_RXED_BOTTOM                  0x0150
+#define OCTETS_RXED_TOP                     0x0154
+#define FRAMES_RXED_OK                      0x0158
+#define BROADCAST_RXED                      0x015C
+#define MULTICAST_RXED                      0x0160
+#define PAUSE_FRAMES_RXED                   0x0164
+#define FRAMES_RXED_64                      0x0168
+#define FRAMES_RXED_65                      0x016C
+#define FRAMES_RXED_128                     0x0170
+#define FRAMES_RXED_256                     0x0174
+#define FRAMES_RXED_512                     0x0178
+#define FRAMES_RXED_1024                    0x017C
+#define FRAMES_RXED_1519                    0x0180
+#define UNDERSIZE_FRAMES                    0x0184
+#define EXCESSIVE_RX_LENGTH                 0x0188
+#define RX_JABBERS                          0x018C
+#define FCS_ERRORS                          0x0190
+#define RX_LENGTH_ERRORS                    0x0194
+#define RX_SYMBOL_ERRORS                    0x0198
+#define ALIGNMENT_ERRORS                    0x019C
+#define RX_RESOURCE_ERRORS                  0x01A0
+#define RX_OVERRUNS                         0x01A4
+#define RX_IP_CK_ERRORS                     0x01A8
+#define RX_TCP_CK_ERRORS                    0x01AC
+#define RX_UDP_CK_ERRORS                    0x01B0
+#define AUTO_FLUSHED_PKTS                   0x01B4
+#define TSU_TIMER_INCR_SUB_NSEC             0x01BC
+#define TSU_TIMER_MSB_SEC                   0x01C0
+#define TSU_STROBE_MSB_SEC                  0x01C4
+#define TSU_STROBE_SEC                      0x01C8
+#define TSU_STROBE_NSEC                     0x01CC
+#define TSU_TIMER_SEC                       0x01D0
+#define TSU_TIMER_NSEC                      0x01D4
+#define TSU_TIMER_ADJUST                    0x01D8
+#define TSU_TIMER_INCR                      0x01DC
+#define TSU_PTP_TX_SEC                      0x01E0
+#define TSU_PTP_TX_NSEC                     0x01E4
+#define TSU_PTP_RX_SEC                      0x01E8
+#define TSU_PTP_RX_NSEC                     0x01EC
+#define TSU_PEER_TX_SEC                     0x01F0
+#define TSU_PEER_TX_NSEC                    0x01F4
+#define TSU_PEER_RX_SEC                     0x01F8
+#define TSU_PEER_RX_NSEC                    0x01FC
+#define PCS_CONTROL                         0x0200
+#define PFC_STATUS                          0x026C
+#define RX_LPI                              0x0270
+#define RX_LPI_TIME                         0x0274
+#define TX_LPI                              0x0278
+#define TX_LPI_TIME                         0x027C
+#define DESIGNCFG_DEBUG1                    0x0280
+#define DESIGNCFG_DEBUG2                    0x0284
+#define DESIGNCFG_DEBUG3                    0x0288
+#define DESIGNCFG_DEBUG4                    0x028C
+#define DESIGNCFG_DEBUG5                    0x0290
+#define DESIGNCFG_DEBUG6                    0x0294
+#define DESIGNCFG_DEBUG7                    0x0298
+#define DESIGNCFG_DEBUG8                    0x029C
+#define DESIGNCFG_DEBUG9                    0x02A0
+#define DESIGNCFG_DEBUG10                   0x02A4
+#define DESIGNCFG_DEBUG11                   0x02A8
+#define DESIGNCFG_DEBUG12                   0x02AC
+#define AXI_QOS_CFG_0                       0x02E0
+#define AXI_QOS_CFG_1                       0x02E4
+#define AXI_QOS_CFG_2                       0x02E8
+#define AXI_QOS_CFG_3                       0x02EC
+#define INT_Q1_STATUS                       0x0400
+#define INT_Q2_STATUS                       0x0404
+#define INT_Q3_STATUS                       0x0408
+#define INT_Q4_STATUS                       0x040C
+#define INT_Q5_STATUS                       0x0410
+#define INT_Q6_STATUS                       0x0414
+#define INT_Q7_STATUS                       0x0418
+#define INT_Q8_STATUS                       0x041C
+#define INT_Q9_STATUS                       0x0420
+#define INT_Q10_STATUS                      0x0424
+#define INT_Q11_STATUS                      0x0428
+#define INT_Q12_STATUS                      0x042C
+#define INT_Q13_STATUS                      0x0430
+#define INT_Q14_STATUS                      0x0434
+#define INT_Q15_STATUS                      0x0438
+#define TRANSMIT_Q1_PTR                     0x0440
+#define TRANSMIT_Q2_PTR                     0x0444
+#define TRANSMIT_Q3_PTR                     0x0448
+#define TRANSMIT_Q4_PTR                     0x044C
+#define TRANSMIT_Q5_PTR                     0x0450
+#define TRANSMIT_Q6_PTR                     0x0454
+#define TRANSMIT_Q7_PTR                     0x0458
+#define TRANSMIT_Q8_PTR                     0x045C
+#define TRANSMIT_Q9_PTR                     0x0460
+#define TRANSMIT_Q10_PTR                    0x0464
+#define TRANSMIT_Q11_PTR                    0x0468
+#define TRANSMIT_Q12_PTR                    0x046C
+#define TRANSMIT_Q13_PTR                    0x0470
+#define TRANSMIT_Q14_PTR                    0x0474
+#define TRANSMIT_Q15_PTR                    0x0478
+#define RECEIVE_Q1_PTR                      0x0480
+#define RECEIVE_Q2_PTR                      0x0484
+#define RECEIVE_Q3_PTR                      0x0488
+#define RECEIVE_Q4_PTR                      0x048C
+#define RECEIVE_Q5_PTR                      0x0490
+#define RECEIVE_Q6_PTR                      0x0494
+#define RECEIVE_Q7_PTR                      0x0498
+#define DMA_RXBUF_SIZE_Q1                   0x04A0
+#define DMA_RXBUF_SIZE_Q2                   0x04A4
+#define DMA_RXBUF_SIZE_Q3                   0x04A8
+#define DMA_RXBUF_SIZE_Q4                   0x04AC
+#define DMA_RXBUF_SIZE_Q5                   0x04B0
+#define DMA_RXBUF_SIZE_Q6                   0x04B4
+#define DMA_RXBUF_SIZE_Q7                   0x04B8
+#define CBS_CONTROL                         0x04BC
+#define CBS_IDLESLOPE_Q_A                   0x04C0
+#define CBS_IDLESLOPE_Q_B                   0x04C4
+#define UPPER_TX_Q_BASE_ADDR                0x04C8
+#define TX_BD_CONTROL                       0x04CC
+#define RX_BD_CONTROL                       0x04D0
+#define UPPER_RX_Q_BASE_ADDR                0x04D4
+#define WD_COUNTER                          0x04EC
+#define AXI_TX_FULL_THRESH0                 0x04F8
+#define AXI_TX_FULL_THRESH1                 0x04FC
+#define SCREENING_TYPE_1_REGISTER_0         0x0500
+#define SCREENING_TYPE_1_REGISTER_1         0x0504
+#define SCREENING_TYPE_1_REGISTER_2         0x0508
+#define SCREENING_TYPE_1_REGISTER_3         0x050C
+#define SCREENING_TYPE_1_REGISTER_4         0x0510
+#define SCREENING_TYPE_1_REGISTER_5         0x0514
+#define SCREENING_TYPE_1_REGISTER_6         0x0518
+#define SCREENING_TYPE_1_REGISTER_7         0x051C
+#define SCREENING_TYPE_1_REGISTER_8         0x0520
+#define SCREENING_TYPE_1_REGISTER_9         0x0524
+#define SCREENING_TYPE_1_REGISTER_10        0x0528
+#define SCREENING_TYPE_1_REGISTER_11        0x052C
+#define SCREENING_TYPE_1_REGISTER_12        0x0530
+#define SCREENING_TYPE_1_REGISTER_13        0x0534
+#define SCREENING_TYPE_1_REGISTER_14        0x0538
+#define SCREENING_TYPE_1_REGISTER_15        0x053C
+#define SCREENING_TYPE_2_REGISTER_0         0x0540
+#define SCREENING_TYPE_2_REGISTER_1         0x0544
+#define SCREENING_TYPE_2_REGISTER_2         0x0548
+#define SCREENING_TYPE_2_REGISTER_3         0x054C
+#define SCREENING_TYPE_2_REGISTER_4         0x0550
+#define SCREENING_TYPE_2_REGISTER_5         0x0554
+#define SCREENING_TYPE_2_REGISTER_6         0x0558
+#define SCREENING_TYPE_2_REGISTER_7         0x055C
+#define SCREENING_TYPE_2_REGISTER_8         0x0560
+#define SCREENING_TYPE_2_REGISTER_9         0x0564
+#define SCREENING_TYPE_2_REGISTER_10        0x0568
+#define SCREENING_TYPE_2_REGISTER_11        0x056C
+#define SCREENING_TYPE_2_REGISTER_12        0x0570
+#define SCREENING_TYPE_2_REGISTER_13        0x0574
+#define SCREENING_TYPE_2_REGISTER_14        0x0578
+#define SCREENING_TYPE_2_REGISTER_15        0x057C
+#define TX_SCHED_CTRL                       0x0580
+#define BW_RATE_LIMIT_Q0TO3                 0x0590
+#define BW_RATE_LIMIT_Q4TO7                 0x0594
+#define BW_RATE_LIMIT_Q8TO11                0x0598
+#define BW_RATE_LIMIT_Q12TO15               0x059C
+#define TX_Q_SEG_ALLOC_Q_LOWER              0x05A0
+#define TX_Q_SEG_ALLOC_Q_UPPER              0x05A4
+#define RECEIVE_Q8_PTR                      0x05C0
+#define RECEIVE_Q9_PTR                      0x05C4
+#define RECEIVE_Q10_PTR                     0x05C8
+#define RECEIVE_Q11_PTR                     0x05CC
+#define RECEIVE_Q12_PTR                     0x05D0
+#define RECEIVE_Q13_PTR                     0x05D4
+#define RECEIVE_Q14_PTR                     0x05D8
+#define RECEIVE_Q15_PTR                     0x05DC
+#define DMA_RXBUF_SIZE_Q8                   0x05E0
+#define DMA_RXBUF_SIZE_Q9                   0x05E4
+#define DMA_RXBUF_SIZE_Q10                  0x05E8
+#define DMA_RXBUF_SIZE_Q11                  0x05EC
+#define DMA_RXBUF_SIZE_Q12                  0x05F0
+#define DMA_RXBUF_SIZE_Q13                  0x05F4
+#define DMA_RXBUF_SIZE_Q14                  0x05F8
+#define DMA_RXBUF_SIZE_Q15                  0x05FC
+#define INT_Q1_ENABLE                       0x0600
+#define INT_Q2_ENABLE                       0x0604
+#define INT_Q3_ENABLE                       0x0608
+#define INT_Q4_ENABLE                       0x060C
+#define INT_Q5_ENABLE                       0x0610
+#define INT_Q6_ENABLE                       0x0614
+#define INT_Q7_ENABLE                       0x0618
+#define INT_Q1_DISABLE                      0x0620
+#define INT_Q2_DISABLE                      0x0624
+#define INT_Q3_DISABLE                      0x0628
+#define INT_Q4_DISABLE                      0x062C
+#define INT_Q5_DISABLE                      0x0630
+#define INT_Q6_DISABLE                      0x0634
+#define INT_Q7_DISABLE                      0x0638
+#define INT_Q1_MASK                         0x0640
+#define INT_Q2_MASK                         0x0644
+#define INT_Q3_MASK                         0x0648
+#define INT_Q4_MASK                         0x064C
+#define INT_Q5_MASK                         0x0650
+#define INT_Q6_MASK                         0x0654
+#define INT_Q7_MASK                         0x0658
+#define INT_Q8_ENABLE                       0x0660
+#define INT_Q9_ENABLE                       0x0664
+#define INT_Q10_ENABLE                      0x0668
+#define INT_Q11_ENABLE                      0x066C
+#define INT_Q12_ENABLE                      0x0670
+#define INT_Q13_ENABLE                      0x0674
+#define INT_Q14_ENABLE                      0x0678
+#define INT_Q15_ENABLE                      0x067C
+#define INT_Q8_DISABLE                      0x0680
+#define INT_Q9_DISABLE                      0x0684
+#define INT_Q10_DISABLE                     0x0688
+#define INT_Q11_DISABLE                     0x068C
+#define INT_Q12_DISABLE                     0x0690
+#define INT_Q13_DISABLE                     0x0694
+#define INT_Q14_DISABLE                     0x0698
+#define INT_Q15_DISABLE                     0x069C
+#define INT_Q8_MASK                         0x06A0
+#define INT_Q9_MASK                         0x06A4
+#define INT_Q10_MASK                        0x06A8
+#define INT_Q11_MASK                        0x06AC
+#define INT_Q12_MASK                        0x06B0
+#define INT_Q13_MASK                        0x06B4
+#define INT_Q14_MASK                        0x06B8
+#define INT_Q15_MASK                        0x06BC
+#define SCREENING_TYPE_2_ETHERTYPE_REG_0    0x06E0
+#define SCREENING_TYPE_2_ETHERTYPE_REG_1    0x06E4
+#define SCREENING_TYPE_2_ETHERTYPE_REG_2    0x06E8
+#define SCREENING_TYPE_2_ETHERTYPE_REG_3    0x06EC
+#define SCREENING_TYPE_2_ETHERTYPE_REG_4    0x06F0
+#define SCREENING_TYPE_2_ETHERTYPE_REG_5    0x06F4
+#define SCREENING_TYPE_2_ETHERTYPE_REG_6    0x06F8
+#define SCREENING_TYPE_2_ETHERTYPE_REG_7    0x06FC
+#define TYPE2_COMPARE_0_WORD_0              0x0700
+#define TYPE2_COMPARE_0_WORD_1              0x0704
+#define TYPE2_COMPARE_1_WORD_0              0x0708
+#define TYPE2_COMPARE_1_WORD_1              0x070C
+#define TYPE2_COMPARE_2_WORD_0              0x0710
+#define TYPE2_COMPARE_2_WORD_1              0x0714
+#define TYPE2_COMPARE_3_WORD_0              0x0718
+#define TYPE2_COMPARE_3_WORD_1              0x071C
+#define TYPE2_COMPARE_4_WORD_0              0x0720
+#define TYPE2_COMPARE_4_WORD_1              0x0724
+#define TYPE2_COMPARE_5_WORD_0              0x0728
+#define TYPE2_COMPARE_5_WORD_1              0x072C
+#define TYPE2_COMPARE_6_WORD_0              0x0730
+#define TYPE2_COMPARE_6_WORD_1              0x0734
+#define TYPE2_COMPARE_7_WORD_0              0x0738
+#define TYPE2_COMPARE_7_WORD_1              0x073C
+#define TYPE2_COMPARE_8_WORD_0              0x0740
+#define TYPE2_COMPARE_8_WORD_1              0x0744
+#define TYPE2_COMPARE_9_WORD_0              0x0748
+#define TYPE2_COMPARE_9_WORD_1              0x074C
+#define TYPE2_COMPARE_10_WORD_0             0x0750
+#define TYPE2_COMPARE_10_WORD_1             0x0754
+#define TYPE2_COMPARE_11_WORD_0             0x0758
+#define TYPE2_COMPARE_11_WORD_1             0x075C
+#define TYPE2_COMPARE_12_WORD_0             0x0760
+#define TYPE2_COMPARE_12_WORD_1             0x0764
+#define TYPE2_COMPARE_13_WORD_0             0x0768
+#define TYPE2_COMPARE_13_WORD_1             0x076C
+#define TYPE2_COMPARE_14_WORD_0             0x0770
+#define TYPE2_COMPARE_14_WORD_1             0x0774
+#define TYPE2_COMPARE_15_WORD_0             0x0778
+#define TYPE2_COMPARE_15_WORD_1             0x077C
+#define TYPE2_COMPARE_16_WORD_0             0x0780
+#define TYPE2_COMPARE_16_WORD_1             0x0784
+#define TYPE2_COMPARE_17_WORD_0             0x0788
+#define TYPE2_COMPARE_17_WORD_1             0x078C
+#define TYPE2_COMPARE_18_WORD_0             0x0790
+#define TYPE2_COMPARE_18_WORD_1             0x0794
+#define TYPE2_COMPARE_19_WORD_0             0x0798
+#define TYPE2_COMPARE_19_WORD_1             0x079C
+#define TYPE2_COMPARE_20_WORD_0             0x07A0
+#define TYPE2_COMPARE_20_WORD_1             0x07A4
+#define TYPE2_COMPARE_21_WORD_0             0x07A8
+#define TYPE2_COMPARE_21_WORD_1             0x07AC
+#define TYPE2_COMPARE_22_WORD_0             0x07B0
+#define TYPE2_COMPARE_22_WORD_1             0x07B4
+#define TYPE2_COMPARE_23_WORD_0             0x07B8
+#define TYPE2_COMPARE_23_WORD_1             0x07BC
+#define TYPE2_COMPARE_24_WORD_0             0x07C0
+#define TYPE2_COMPARE_24_WORD_1             0x07C4
+#define TYPE2_COMPARE_25_WORD_0             0x07C8
+#define TYPE2_COMPARE_25_WORD_1             0x07CC
+#define TYPE2_COMPARE_26_WORD_0             0x07D0
+#define TYPE2_COMPARE_26_WORD_1             0x07D4
+#define TYPE2_COMPARE_27_WORD_0             0x07D8
+#define TYPE2_COMPARE_27_WORD_1             0x07DC
+#define TYPE2_COMPARE_28_WORD_0             0x07E0
+#define TYPE2_COMPARE_28_WORD_1             0x07E4
+#define TYPE2_COMPARE_29_WORD_0             0x07E8
+#define TYPE2_COMPARE_29_WORD_1             0x07EC
+#define TYPE2_COMPARE_30_WORD_0             0x07F0
+#define TYPE2_COMPARE_30_WORD_1             0x07F4
+#define TYPE2_COMPARE_31_WORD_0             0x07F8
+#define TYPE2_COMPARE_31_WORD_1             0x07FC
+#define ENST_START_TIME_Q8                  0x0800
+#define ENST_START_TIME_Q9                  0x0804
+#define ENST_START_TIME_Q10                 0x0808
+#define ENST_START_TIME_Q11                 0x080C
+#define ENST_START_TIME_Q12                 0x0810
+#define ENST_START_TIME_Q13                 0x0814
+#define ENST_START_TIME_Q14                 0x0818
+#define ENST_START_TIME_Q15                 0x081C
+#define ENST_ON_TIME_Q8                     0x0820
+#define ENST_ON_TIME_Q9                     0x0824
+#define ENST_ON_TIME_Q10                    0x0828
+#define ENST_ON_TIME_Q11                    0x082C
+#define ENST_ON_TIME_Q12                    0x0830
+#define ENST_ON_TIME_Q13                    0x0834
+#define ENST_ON_TIME_Q14                    0x0838
+#define ENST_ON_TIME_Q15                    0x083C
+#define ENST_OFF_TIME_Q8                    0x0840
+#define ENST_OFF_TIME_Q9                    0x0844
+#define ENST_OFF_TIME_Q10                   0x0848
+#define ENST_OFF_TIME_Q11                   0x084C
+#define ENST_OFF_TIME_Q12                   0x0850
+#define ENST_OFF_TIME_Q13                   0x0854
+#define ENST_OFF_TIME_Q14                   0x0858
+#define ENST_OFF_TIME_Q15                   0x085C
+#define ENST_CONTROL                        0x0880
+#define RX_Q0_FLUSH                         0x0B00
+#define RX_Q1_FLUSH                         0x0B04
+#define RX_Q2_FLUSH                         0x0B08
+#define RX_Q3_FLUSH                         0x0B0C
+#define RX_Q4_FLUSH                         0x0B10
+#define RX_Q5_FLUSH                         0x0B14
+#define RX_Q6_FLUSH                         0x0B18
+#define RX_Q7_FLUSH                         0x0B1C
+#define RX_Q8_FLUSH                         0x0B20
+#define RX_Q9_FLUSH                         0x0B24
+#define RX_Q10_FLUSH                        0x0B28
+#define RX_Q11_FLUSH                        0x0B2C
+#define RX_Q12_FLUSH                        0x0B30
+#define RX_Q13_FLUSH                        0x0B34
+#define RX_Q14_FLUSH                        0x0B38
+#define RX_Q15_FLUSH                        0x0B3C
+#define SCR2_REG0_RATE_LIMIT                0x0B40
+#define SCR2_REG1_RATE_LIMIT                0x0B44
+#define SCR2_REG2_RATE_LIMIT                0x0B48
+#define SCR2_REG3_RATE_LIMIT                0x0B4C
+#define SCR2_REG4_RATE_LIMIT                0x0B50
+#define SCR2_REG5_RATE_LIMIT                0x0B54
+#define SCR2_REG6_RATE_LIMIT                0x0B58
+#define SCR2_REG7_RATE_LIMIT                0x0B5C
+#define SCR2_REG8_RATE_LIMIT                0x0B60
+#define SCR2_REG9_RATE_LIMIT                0x0B64
+#define SCR2_REG10_RATE_LIMIT               0x0B68
+#define SCR2_REG11_RATE_LIMIT               0x0B6C
+#define SCR2_REG12_RATE_LIMIT               0x0B70
+#define SCR2_REG13_RATE_LIMIT               0x0B74
+#define SCR2_REG14_RATE_LIMIT               0x0B78
+#define SCR2_REG15_RATE_LIMIT               0x0B7C
+#define SCR2_RATE_STATUS                    0x0B80
+#define ASF_INT_STATUS                      0x0E00
+#define ASF_INT_RAW_STATUS                  0x0E04
+#define ASF_INT_MASK                        0x0E08
+#define ASF_INT_TEST                        0x0E0C
+#define ASF_FATAL_NONFATAL_SELECT           0x0E10
+#define ASF_TRANS_TO_FAULT_MASK             0x0E34
+#define ASF_TRANS_TO_FAULT_STATUS           0x0E38
+#define ASF_PROTOCOL_FAULT_MASK             0x0E40
+#define ASF_PROTOCOL_FAULT_STATUS           0x0E44
+
+/* Register bit field definitions *******************************************/
+
+/* NETWORK_CONTROL:
+ * The network control register contains general MAC control functions
+ * for both receiver and transmitter.
+ */
+
+#define NETWORK_CONTROL_LOOPBACK                                (1 << 0)
+#define NETWORK_CONTROL_LOOPBACK_LOCAL                          (1 << 1)
+#define NETWORK_CONTROL_ENABLE_RECEIVE                          (1 << 2)
+#define NETWORK_CONTROL_ENABLE_TRANSMIT                         (1 << 3)
+#define NETWORK_CONTROL_MAN_PORT_EN                             (1 << 4)
+#define NETWORK_CONTROL_CLEAR_ALL_STATS_REGS                    (1 << 5)
+#define NETWORK_CONTROL_INC_ALL_STATS_REGS                      (1 << 6)
+#define NETWORK_CONTROL_STATS_WRITE_EN                          (1 << 7)
+#define NETWORK_CONTROL_BACK_PRESSURE                           (1 << 8)
+#define NETWORK_CONTROL_TRANSMIT_START                          (1 << 9)
+#define NETWORK_CONTROL_TRANSMIT_HALT                           (1 << 10)
+#define NETWORK_CONTROL_TX_PAUSE_FRAME_REQ                      (1 << 11)
+#define NETWORK_CONTROL_TX_PAUSE_FRAME_ZERO                     (1 << 12)
+#define NETWORK_CONTROL_STORE_RX_TS                             (1 << 15)
+#define NETWORK_CONTROL_PFC_ENABLE                              (1 << 16)
+#define NETWORK_CONTROL_TRANSMIT_PFC_PRIORITY_BASED_PAUSE_FRAME (1 << 17)
+#define NETWORK_CONTROL_FLUSH_RX_PKT_PCLK                       (1 << 18)
+#define NETWORK_CONTROL_TX_LPI_EN                               (1 << 19)
+#define NETWORK_CONTROL_PTP_UNICAST_ENA                         (1 << 20)
+#define NETWORK_CONTROL_ALT_SGMII_MODE                          (1 << 21)
+#define NETWORK_CONTROL_STORE_UDP_OFFSET                        (1 << 22)
+#define NETWORK_CONTROL_EXT_TSU_PORT_ENABLE                     (1 << 23)
+#define NETWORK_CONTROL_ONE_STEP_SYNC_MODE                      (1 << 24)
+#define NETWORK_CONTROL_PFC_CTRL                                (1 << 25)
+#define NETWORK_CONTROL_EXT_RXQ_SEL_EN                          (1 << 26)
+#define NETWORK_CONTROL_OSS_CORRECTION_FIELD                    (1 << 27)
+#define NETWORK_CONTROL_SEL_MII_ON_RGMII                        (1 << 28)
+#define NETWORK_CONTROL_TWO_PT_FIVE_GIG                         (1 << 29)
+#define NETWORK_CONTROL_IFG_EATS_QAV_CREDIT                     (1 << 30)
+
+/* NETWORK_CONFIG:
+ * The network configuration register contains functions for
+ * setting the mode of operation for the Gigabit Ethernet MAC.
+ */
+
+#define NETWORK_CONFIG_SPEED                                    (1 << 0)
+#define NETWORK_CONFIG_FULL_DUPLEX                              (1 << 1)
+#define NETWORK_CONFIG_DISCARD_NON_VLAN_FRAMES                  (1 << 2)
+#define NETWORK_CONFIG_JUMBO_FRAMES                             (1 << 3)
+#define NETWORK_CONFIG_COPY_ALL_FRAMES                          (1 << 4)
+#define NETWORK_CONFIG_NO_BROADCAST                             (1 << 5)
+#define NETWORK_CONFIG_MULTICAST_HASH_ENABLE                    (1 << 6)
+#define NETWORK_CONFIG_UNICAST_HASH_ENABLE                      (1 << 7)
+#define NETWORK_CONFIG_RECEIVE_1536_BYTE_FRAMES                 (1 << 8)
+#define NETWORK_CONFIG_EXTERNAL_ADDRESS_MATCH_ENABLE            (1 << 9)
+#define NETWORK_CONFIG_GIGABIT_MODE_ENABLE                      (1 << 10)
+#define NETWORK_CONFIG_PCS_SELECT                               (1 << 11)
+#define NETWORK_CONFIG_RETRY_TEST                               (1 << 12)
+#define NETWORK_CONFIG_PAUSE_ENABLE                             (1 << 13)
+#define NETWORK_CONFIG_RECEIVE_BUFFER_OFFSET_MASK               (3)
+#define NETWORK_CONFIG_RECEIVE_BUFFER_OFFSET_SHIFT              (14)
+#define NETWORK_CONFIG_LENGTH_FIELD_ERROR_FRAME_DISCARD         (1 << 16)
+#define NETWORK_CONFIG_FCS_REMOVE                               (1 << 17)
+#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT                  (18)
+#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK                   (7)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8                    (0 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16                   (1 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32                   (2 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48                   (3 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64                   (4 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define   NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96                   (5 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT)
+#define NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT                     (21)
+#define NETWORK_CONFIG_DATA_BUS_WIDTH_MASK                      (3)
+#define NETWORK_CONFIG_DISABLE_COPY_OF_PAUSE_FRAMES             (1 << 23)
+#define NETWORK_CONFIG_RECEIVE_CHECKSUM_OFFLOAD_ENABLE          (1 << 24)
+#define NETWORK_CONFIG_EN_HALF_DUPLEX_RX                        (1 << 25)
+#define NETWORK_CONFIG_IGNORE_RX_FCS                            (1 << 26)
+#define NETWORK_CONFIG_SGMII_MODE_ENABLE                        (1 << 27)
+#define NETWORK_CONFIG_IPG_STRETCH_ENABLE                       (1 << 28)
+#define NETWORK_CONFIG_NSP_CHANGE                               (1 << 29)
+#define NETWORK_CONFIG_IGNORE_IPG_RX_ER                         (1 << 30)
+#define NETWORK_CONFIG_UNI_DIRECTION_ENABLE                     (1 << 31)
+
+/* NETWORK_STATUS:
+ * The network status register returns status information with respect
+ * to the PHY management MDIO interface, the PCS, priority flow control,
+ * LPI and other status.
+ */
+
+#define NETWORK_STATUS_PCS_LINK_STATE                           (1 << 0)
+#define NETWORK_STATUS_MDIO_IN                                  (1 << 1)
+#define NETWORK_STATUS_MAN_DONE                                 (1 << 2)
+#define NETWORK_STATUS_MAC_FULL_DUPLEX                          (1 << 3)
+#define NETWORK_STATUS_MAC_PAUSE_RX_EN                          (1 << 4)
+#define NETWORK_STATUS_MAC_PAUSE_TX_EN                          (1 << 5)
+#define NETWORK_STATUS_PFC_NEGOTIATE_PCLK                       (1 << 6)
+#define NETWORK_STATUS_LPI_INDICATE_PCLK                        (1 << 7)
+#define NETWORK_STATUS_AXI_XACTION_OUTSTANDING                  (1 << 8)
+#define NETWORK_STATUS_LINK_FAULT_INDICATION_SHIFT              (1 << 9)
+#define NETWORK_STATUS_LINK_FAULT_INDICATION_MASK               (7)
+
+/* DMA_CONFIG:
+ * DMA Configuration Register
+ */
+
+#define DMA_CONFIG_AMBA_BURST_LENGTH_SHIFT          (0)       /* Bits: 0-4 */
+#define DMA_CONFIG_AMBA_BURST_LENGTH_MASK           (0x1f)
+#define DMA_CONFIG_HDR_DATA_SPLITTING_EN            (1 << 5)  /* Bit 5 */
+#define DMA_CONFIG_ENDIAN_SWAP_MANAGEMENT           (1 << 6)  /* Bit 6 */
+#define DMA_CONFIG_ENDIAN_SWAP_PACKET               (1 << 7)  /* Bit 7 */
+#define DMA_CONFIG_RX_PBUF_SIZE_SHIFT               (8)       /* Bits: 8-9 */
+#define DMA_CONFIG_RX_PBUF_SIZE_MASK                (3)
+#define DMA_CONFIG_TX_PBUF_SIZE                     (1 << 10) /* Bit 10 */
+#define DMA_CONFIG_TX_PBUF_TCP_EN                   (1 << 11) /* Bit 11 */
+#define DMA_CONFIG_INFINITE_LAST_DBUF_SIZE_EN       (1 << 12) /* Bit 12 */
+#define DMA_CONFIG_CRC_ERROR_REPORT                 (1 << 13) /* Bit 13 */
+#define DMA_CONFIG_RX_BUF_SIZE_MASK                 (0xff)
+#define DMA_CONFIG_RX_BUF_SIZE_SHIFT                (16)
+#define DMA_CONFIG_FORCE_DISCARD_ON_ERR             (1 << 24) /* Bit 24 */
+#define DMA_CONFIG_FORCE_MAX_AMBA_BURST_RX          (1 << 25) /* Bit 25 */
+#define DMA_CONFIG_FORCE_MAX_AMBA_BURST_TX          (1 << 26) /* Bit 26 */
+                                                              /* Bit 27 reserved */
+#define DMA_CONFIG_RX_BD_EXTENDED_MODE_EN           (1 << 28) /* Bit 28 */
+#define DMA_CONFIG_TX_BD_EXTENDED_MODE_EN           (1 << 29) /* Bit 29 */
+#define DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1             (1 << 30) /* Bit 30 */
+                                                              /* Bit 31 reserved */
+
+/* TRANSMIT_STATUS:
+ * This register, when read, provides details of the status
+ * of the transmit path. Once read, individual bits may be cleared
+ * by writing 1 to them. It is not possible to set a bit to 1 by writing
+ * to the register.
+ */
+
+#define TRANSMIT_STATUS_USED_BIT_READ               (1 << 0)
+#define TRANSMIT_STATUS_COLLISION_OCCURED           (1 << 1)
+#define TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED        (1 << 2)
+#define TRANSMIT_STATUS_TRANSMIT_GO                 (1 << 3)
+#define TRANSMIT_STATUS_AMBA_ERROR                  (1 << 4)
+#define TRANSMIT_STATUS_TRANSMIT_COMPLETE           (1 << 5)
+#define TRANSMIT_STATUS_UNDERRUN                    (1 << 6)
+#define TRANSMIT_STATUS_LATE_COLLISION              (1 << 7)
+#define TRANSMIT_STATUS_RESP_NOT_OK                 (1 << 8)
+#define TRANSMIT_STATUS_TX_MAC_LOCKUP               (1 << 9)
+#define TRANSMIT_STATUS_TX_DMA_LOCKUP               (1 << 10)
+
+/* RECEIVE_STATUS:
+ * This register, when read, provides details of the status
+ * of the receive path. Once read, individual bits may be cleared
+ * by writing 1 to them. It is not possible to set a bit to 1 by writing
+ * to the register.
+ */
+#define RECEIVE_STATUS_BUFFER_NOT_AVAILABLE         (1 << 0)
+#define RECEIVE_STATUS_FRAME_RECEIVED               (1 << 1)
+#define RECEIVE_STATUS_RECEIVE_OVERRUN              (1 << 2)
+#define RECEIVE_STATUS_RESP_NOT_OK                  (1 << 3)
+#define RECEIVE_STATUS_RX_MAC_LOCKUP                (1 << 4)
+#define RECEIVE_STATUS_RX_DMA_LOCKUP                (1 << 5)
+
+/* PHY_MANAGEMENT:
+ */
+
+#define PHY_MANAGEMENT_PHY_DATA_SHIFT               (0)
+#define PHY_MANAGEMENT_PHY_DATA_MASK                (0xffff)
+#define PHY_MANAGEMENT_PHY_DATA(n)                  ((n & PHY_MANAGEMENT_PHY_DATA_MASK) << PHY_MANAGEMENT_PHY_DATA_SHIFT)
+#define PHY_MANAGEMENT_WRITE10                      (2 << PHY_MANAGEMENT_WRITE10_SHIFT)
+#define PHY_MANAGEMENT_WRITE10_SHIFT                (16)
+#define PHY_MANAGEMENT_WRITE10_MASK                 (3)
+#define PHY_MANAGEMENT_REG_ADDRESS_SHIFT            (18)
+#define PHY_MANAGEMENT_REG_ADDRESS_MASK             (0x1f)
+#define PHY_MANAGEMENT_REG_ADDRESS(n)               ((n & PHY_MANAGEMENT_REG_ADDRESS_MASK ) << PHY_MANAGEMENT_REG_ADDRESS_SHIFT)
+#define PHY_MANAGEMENT_PHY_ADDRESS_SHIFT            (23)
+#define PHY_MANAGEMENT_PHY_ADDRESS_MASK             (0x1f)
+#define PHY_MANAGEMENT_PHY_ADDRESS(n)               ((n & PHY_MANAGEMENT_PHY_ADDRESS_MASK ) << PHY_MANAGEMENT_PHY_ADDRESS_SHIFT)
+#define PHY_MANAGEMENT_OPERATION_SHIFT              (28)
+#define PHY_MANAGEMENT_OPERATION_MASK               (3)
+#  define PHY_MANAGEMENT_OPERATION_READ             (2 << PHY_MANAGEMENT_OPERATION_SHIFT)
+#  define PHY_MANAGEMENT_OPERATION_WRITE            (1 << PHY_MANAGEMENT_OPERATION_SHIFT)
+#define PHY_MANAGEMENT_WRITE_1                      (1 << 30)
+#define PHY_MANAGEMENT_WRITE_0                      (1 << 31)
+
+/* irq status, enable and disable */
+
+#define GEM_INT_MANAGEMENT_DONE                     (1 << 0)
+#define GEM_INT_RECEIVE_COMPLETE                    (1 << 1)
+#define GEM_INT_RECEIVE_BUFFER_USED_BIT_READ        (1 << 2)
+#define GEM_INT_TRANSMIT_BUFFER_USED_BIT_READ       (1 << 3)
+#define GEM_INT_TRANSMIT_BUFFER_UNDERRUN            (1 << 4)
+#define GEM_INT_RETRY_LIMIT_EXCEEDED                (1 << 5)
+#define GEM_INT_AMBA_ERROR                          (1 << 6)
+#define GEM_INT_TRANSMIT_COMPLETE                   (1 << 7)
+#define GEM_INT_RECEIVE_OVERRUN                     (1 << 10)
+#define GEM_INT_RESP_NOT_OK                         (1 << 11)
+
+/* Receive buffer descriptor:  Address word */
+
+#define GEM_RX_DMA_ADDR_OWNER       (1 << 0)     /* Bit 0:  1=Software owns; 0=GMAC owns */
+#define GEM_RX_DMA_ADDR_WRAP        (1 << 1)     /* Bit 1:  Last descriptor in list */
+#define GEM_RX_DMA_ADDR_MASK        (0xfffffffc) /* Bits 2-31: Aligned buffer address */
+
+/* Receive buffer descriptor:  status word */
+
+#define GEM_RX_DMA_STATUS_FRAME_LEN_SHIFT  (0)         /* Bits 0-12: Frame length */
+#define GEM_RX_DMA_STATUS_FRAME_LEN_MASK   (0x00000fff)
+#define GEM_RX_DMA_STATUS_SOF              (1 << 14)   /* Bit 14: Start of frame */
+#define GEM_RX_DMA_STATUS_EOF              (1 << 15)   /* Bit 14: End of frame */
+
+#define GEM_TX_DMA_LAST                    (1 << 15)  /* the last buffer in a frame */
+#define GEM_TX_DMA_NO_CRC                  (1 << 16)  /* don't calc and append crc */
+                                                      /* 17 - 19 reserved */
+#define GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT    (20)       /* */
+#define GEM_TX_DMA_OFFLOAD_ERRORS_MASK     (7)        /* */
+#define GEM_TX_DMA_OFFLOAD_ERROR_OK        (0 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_VLAN      (1 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_SNAP      (2 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_IP        (3 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_UNK       (4 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_FRAG      (5 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_PROTO     (6 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_OFFLOAD_ERROR_END       (7 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT)
+#define GEM_TX_DMA_TS_PRESENT              (1 << 23)  /* */
+#define GEM_TX_DMA_LATE_COL_ERROR          (1 << 26)  /* */
+#define GEM_TX_DMA_BUS_ERROR               (1 << 27)  /* */
+#define GEM_TX_DMA_UNDERRUN                (1 << 28)  /* */
+#define GEM_TX_DMA_RETRY_ERROR             (1 << 29)  /* */
+#define GEM_TX_DMA_WRAP                    (1 << 30)  /* */
+#define GEM_TX_DMA_USED                    (1 << 31)  /* */

Review comment:
       done.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829391902



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))

Review comment:
       like in linux kernel:
   A basic shift-right of a 64- or 32-bit quantity.  Use this to suppress the "right shift count >= width of type" warning when that quantity is 32-bits.
   sadly this macro don't exist in any nuttx headrers.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jlaitine commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jlaitine commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829067542



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       I guess that the idea is that by defining CONFIG_NETDEV_LATEINIT you can call mpfs_ethinitialize in you own board files, and the call to the riscv_netinitialize is suppressed in the common code. This is the same as what is done for the arm boards - typically you just initialize it via the common riscv_initialize, but can make an exception by defining CONFIG_NETDEV_LATEINIT and doing your own initialization.
   
   Of course, we could have the header file for these function prototypes (mpfs_ethinitialize and mpfs_phy_boardinitialize) similarly as what is done for arm boards (e.g. stm32_ethernet.h).
   
   
   




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829086104



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       Lets got with `int mpfs_ethinitialize(int intf);` only and remove `static` case

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))

Review comment:
       Thank you for explanation

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       Lets go with `int mpfs_ethinitialize(int intf);` only and remove `static` case

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3837 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */

Review comment:
       2 spaces alignment

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3837 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t              *rxbuffer;        /* Allocated RX buffers */
+        uint8_t              *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t             txhead;           /* Circular buffer head index */
+        uint32_t             txtail;           /* Circular buffer tail index */
+        uint32_t             rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t    *int_status;      /* interrupt status */
+        volatile uint32_t    *int_mask;        /* interrupt mask */
+        volatile uint32_t    *int_enable;      /* interrupt enable */
+        volatile uint32_t    *int_disable;     /* interrupt disable */
+        volatile uint32_t    *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t    *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t    *dma_rxbuf_size;  /* RX queue buffer size */

Review comment:
       2 spaces alignment

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3823 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;    /* Buffer address */
+    uint32_t status;  /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi; /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;    /* Buffer address */
+    uint32_t status;  /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi; /* Buffer address high 32-bits */
+    uint32_t unused;  /*  */

Review comment:
       2 spaces indentation.

##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3837 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t              *rxbuffer;        /* Allocated RX buffers */
+        uint8_t              *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t             txhead;           /* Circular buffer head index */
+        uint32_t             txtail;           /* Circular buffer tail index */
+        uint32_t             rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t    *int_status;      /* interrupt status */
+        volatile uint32_t    *int_mask;        /* interrupt mask */
+        volatile uint32_t    *int_enable;      /* interrupt enable */
+        volatile uint32_t    *int_disable;     /* interrupt disable */
+        volatile uint32_t    *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t    *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t    *dma_rxbuf_size;  /* RX queue buffer size */

Review comment:
       Please address




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jlaitine commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jlaitine commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r826969741



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3855 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  (void)priv;
+  (void)irq;
+  (void)context;
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+  #ifdef CONFIG_NET_IPv6
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  volatile unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].tx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rx_desc_tab)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].txbuffer)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (!priv->queue[queue].rxbuffer)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == (void *)MPFS_GEM0_LO_BASE)

Review comment:
       No need to cast to void *, comparing uintptr_t
   ```suggestion
     if (priv->regbase == MPFS_GEM0_LO_BASE)
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jlaitine commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jlaitine commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r826968350



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3855 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)priv->regbase + offset;

Review comment:
       offset is in bytes, this is a bug




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jlaitine commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jlaitine commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829068162



##########
File path: boards/risc-v/bl602/bl602evb/configs/wifi/defconfig
##########
@@ -73,6 +73,7 @@ CONFIG_MTD=y
 CONFIG_MTD_PARTITION=y
 CONFIG_NET=y
 CONFIG_NETDB_DNSCLIENT=y
+CONFIG_NETDEV_LATEINIT=y

Review comment:
       since those architectures don't implement the "standard" up/riscv_netinitialize, but they initialize the network in board files directly with some custom interface. 




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jlaitine commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jlaitine commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829067542



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       I guess that the idea is that by defining CONFIG_NETDEV_LATEINIT you can call mpfs_ethinitialize in you own board files, and the call to the riscv_netinitialize is suppressed in the common code. This is the same as what is done for the arm boards - typically you just initialize it via the common riscv_initialize, but can make an exception by defining CONFIG_NETDEV_LATEINIT and doing your own initialization.
   
   Of course, we could have a header file for these function prototypes (mpfs_ethinitialize and mpfs_phy_boardinitialize) similarly as what is done for arm boards (e.g. stm32_ethernet.h).
   
   
   




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] jrosberg commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
jrosberg commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829070796



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       Yes.
   This is preferred way to disable call to riscv_netinitialize() on early boot.
   is CONFIG_NETDEV_LATEINIT is defined then riscv_netinitialize() is defined as empty.
   exactly same as on any arm targets.
   I will add the mpfs_ethernet.h file for prototypes.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5740: Add ethernet support for risc-v/MPFS

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5740:
URL: https://github.com/apache/incubator-nuttx/pull/5740#discussion_r829086104



##########
File path: arch/risc-v/src/mpfs/mpfs_ethernet.c
##########
@@ -0,0 +1,3841 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_ethernet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you 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
+ *
+ *   http://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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/gmii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+#  include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "hardware/mpfs_ethernet.h"
+#include "mpfs_memorymap.h"
+
+#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC)
+
+#ifndef OK
+#  define OK 0
+#endif
+
+#define MPFS_PMPCFG_ETH0_0   (MPFS_MPUCFG_BASE + 0x400)
+#define MPFS_PMPCFG_ETH0_1   (MPFS_MPUCFG_BASE + 0x408)
+#define MPFS_PMPCFG_ETH0_2   (MPFS_MPUCFG_BASE + 0x410)
+#define MPFS_PMPCFG_ETH0_3   (MPFS_MPUCFG_BASE + 0x418)
+#define MPFS_PMPCFG_ETH1_0   (MPFS_MPUCFG_BASE + 0x500)
+#define MPFS_PMPCFG_ETH1_1   (MPFS_MPUCFG_BASE + 0x508)
+#define MPFS_PMPCFG_ETH1_2   (MPFS_MPUCFG_BASE + 0x510)
+#define MPFS_PMPCFG_ETH1_3   (MPFS_MPUCFG_BASE + 0x518)
+
+#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1)
+#  warning "Using 2 MACs is not yet supported."
+#  define MPFS_NETHERNET     (2)
+#else
+#  define MPFS_NETHERNET     (1)
+#endif
+
+#define MPFS_MAC_QUEUE_COUNT (4)
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#else
+
+/* Select work queue */
+
+#  if defined(CONFIG_MPFS_ETHMAC_HPWORK)
+#    define ETHWORK HPWORK
+#  elif defined(CONFIG_MPFS_ETHMAC_LPWORK)
+#    define ETHWORK LPWORK
+#  else
+#    define ETHWORK LPWORK
+#  endif
+#endif
+
+#ifndef CONFIG_MPFS_PHYADDR
+#  error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII)
+#  warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII)
+#  error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined"
+#endif
+
+/* Number of buffers for RX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NRXBUFFERS  (16)
+#endif
+
+/* Number of buffers for TX */
+
+#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS
+#  define CONFIG_MPFS_ETHMAC_NTXBUFFERS  (8)
+#endif
+
+/* GMAC buffer sizes
+ * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable?
+ * issue when using the MTU size receive block
+ */
+
+#define GMAC_RX_UNITSIZE  (512)                  /* Fixed size for RX buffer */
+#define GMAC_TX_UNITSIZE  CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */
+
+/* The MAC can support frame lengths up to 1536 bytes */
+
+#define GMAC_MAX_FRAMELEN (1536)
+#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN
+#  error CONFIG_NET_ETH_PKTSIZE is too large
+#endif
+
+/* for DMA dma_rxbuf_size */
+
+#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64)
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1)
+
+#ifdef CONFIG_NET_DUMPPACKET
+#  define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+#  define mpfs_dumppacket(m,a,n)
+#endif
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+#  define CORE_RMII_RESET       (1 << 15)
+#  define CORE_RMII_LOOPBACK    (1 << 2)
+#  define CORE_RMII_FULL_DUPLEX (1 << 1)
+#  define CORE_RMII_100MBIT     (1 << 0)
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds.
+ * CLK_TCK is the number of clock ticks per second
+ */
+
+#define MPFS_WDDELAY     (1 * CLK_TCK)
+
+/* TX timeout = 1 minute */
+
+#define MPFS_TXTIMEOUT   (60 * CLK_TCK)
+
+/* PHY reset tim in loop counts */
+
+#define PHY_RESET_WAIT_COUNT (10)
+
+/* PHY re-try coount in loop counts */
+
+#define PHY_RETRY_MAX   300000
+
+/* This is a helper pointer for accessing the contents of the GMAC header */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16))
+#define lower_32_bits(n) ((uint32_t)(n))
+
+/* Interrupt flags */
+
+#define INT_RX     (GEM_INT_RECEIVE_COMPLETE)
+#define INT_TX     (GEM_INT_TRANSMIT_COMPLETE)
+#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \
+                    GEM_INT_RECEIVE_OVERRUN | \
+                    GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK)
+#define INT_ALL    (INT_RX | INT_TX | INT_ERRORS)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct gmac_rxdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* RX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;
+#endif
+};
+
+/* Transmit buffer descriptor */
+
+struct gmac_txdesc_s
+{
+    uint32_t addr;     /* Buffer address */
+    uint32_t status;   /* TX status and controls */
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+    uint32_t addr_hi;  /* Buffer address high 32-bits */
+    uint32_t unused;   /*  */
+#endif
+};
+
+static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE];
+
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]
+               aligned_data(8);
+static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE]
+               aligned_data(8);
+#endif
+
+struct mpfs_mac_queue_s
+{
+#if defined(CONFIG_MPFS_GMAC_PREALLOCATE)
+        struct gmac_txdesc_s  tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS];  /* Transmit descriptor table */
+        struct gmac_rxdesc_s  rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS];  /* Receive descriptor table */
+#else
+        struct gmac_txdesc_s  *tx_desc_tab;     /* Transmit descriptor table */
+        struct gmac_rxdesc_s  *rx_desc_tab;     /* Receive descriptor table */
+        uint8_t               *rxbuffer;        /* Allocated RX buffers */
+        uint8_t               *txbuffer;        /* Allocated TX buffers */
+#endif
+        uint32_t              txhead;           /* Circular buffer head index */
+        uint32_t              txtail;           /* Circular buffer tail index */
+        uint32_t              rxndx;            /* RX index for current processing RX descriptor */
+        volatile uint32_t     *int_status;      /* interrupt status */
+        volatile uint32_t     *int_mask;        /* interrupt mask */
+        volatile uint32_t     *int_enable;      /* interrupt enable */
+        volatile uint32_t     *int_disable;     /* interrupt disable */
+        volatile uint32_t     *rx_q_ptr;        /* RX queue pointer */
+        volatile uint32_t     *tx_q_ptr;        /* TX queue pointer */
+        volatile uint32_t     *dma_rxbuf_size;  /* RX queue buffer size */
+};
+
+/* The mpfs_ethmac_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct mpfs_ethmac_s
+{
+  uintptr_t     regbase;                         /* mac base address */
+  irq_t         mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */
+  uint8_t       ifup : 1;                        /* true:ifup false:ifdown */
+  uint8_t       intf;                            /* Ethernet interface number */
+  uint8_t       phyaddr;                         /* PHY address */
+  struct wdog_s txpoll;                          /* TX poll timer */
+  struct wdog_s txtimeout;                       /* TX timeout timer */
+  struct work_s irqwork;                         /* For deferring interrupt work to the work queue */
+  struct work_s pollwork;                        /* For deferring poll work to the work queue */
+
+  /* This holds the information visible to the NuttX network */
+
+  struct net_driver_s     dev;  /* Interface understood by the network */
+  struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT];
+};
+
+/* These are the pre-allocated Ethernet device structures */
+
+static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET];
+
+static uintptr_t g_regbases[MPFS_NETHERNET] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  MPFS_GEM0_LO_BASE,
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  MPFS_GEM1_LO_BASE,
+#endif
+  /* if support for emacs is added later
+   * (MPFS_GEM0_LO_BASE + 0x1000),
+   * (MPFS_GEM1_LO_BASE + 0x1000),'
+   */
+};
+
+static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] =
+{
+#ifdef CONFIG_MPFS_ETHMAC_0
+  { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1,
+    MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 },
+#endif
+#ifdef CONFIG_MPFS_ETHMAC_1
+  { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1,
+    MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 },
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue);
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue);
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue);
+
+static void mpfs_poll_expiry(wdparm_t arg);
+static void mpfs_poll_work(void *arg);
+
+static int  mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue);
+static int  mpfs_txpoll(struct net_driver_s *dev);
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv);
+
+/* NuttX callback functions */
+
+static int  mpfs_ifup(struct net_driver_s *dev);
+static int  mpfs_ifdown(struct net_driver_s *dev);
+
+static void mpfs_txavail_work(void *arg);
+static int  mpfs_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int  mpfs_ioctl(struct net_driver_s *dev, int cmd,
+                       unsigned long arg);
+#endif
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac);
+static int  mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg);
+#endif
+
+/* PHY Initialization */
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv);
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyinit(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t *phyval);
+static int  mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                          uint8_t regaddr, uint16_t phyval);
+static int  mpfs_phywait(struct mpfs_ethmac_s *priv);
+static int  mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr);
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int  mpfs_autonegotiate(struct mpfs_ethmac_s *priv);
+#else
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv);
+#endif
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv);
+#else
+#  define mpfs_phydump(priv)
+#endif
+
+/* MAC/DMA Initialization */
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv);
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv);
+static int  mpfs_macenable(struct mpfs_ethmac_s *priv);
+static int  mpfs_ethconfig(struct mpfs_ethmac_s *priv);
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void mpfs_ipv6multicast(struct sam_gmac_s *priv);
+#endif
+
+static void mpfs_interrupt_work(void *arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mac_putreg
+ *
+ * Description:
+ *  Write a value to an MAC register
+ *
+ * Input Parameters:
+ *   val - The value to write to the register
+ *   offset - The mac register address offset to write
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void mac_putreg(struct mpfs_ethmac_s *priv,
+                              uint32_t offset, uint32_t val)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x <- 0x%08x\n", addr, val);
+#endif
+  putreg32(val, addr);
+}
+
+/****************************************************************************
+ * Name: mac_getreg
+ *
+ * Description:
+ *   Read a value from an MAC register
+ *
+ * Input Parameters:
+ *   priv -
+ *   offset - The register address offset to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv,
+                                  uint32_t offset)
+{
+  uint32_t *addr = (uint32_t *)(priv->regbase + offset);
+  uint32_t value = getreg32(addr);
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG)
+  ninfo("0x%08x -> 0x%08x\n", addr, value);
+#endif
+  return value;
+}
+
+static int mpfs_interrupt_0(int irq, void *context, void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  UNUSED(irq);
+  UNUSED(context);
+
+  /* Disable further Ethernet interrupts. */
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  isr = *priv->queue[0].int_status;
+  ninfo("isr=0x%" PRIx32 "\n", isr);
+
+  /* clear all pending... */
+
+  *priv->queue[0].int_status = isr;
+
+  if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0)
+    {
+      /* If a TX transfer just completed, then cancel the TX timeout */
+
+      nwarn("TX complete: cancel timeout\n");
+      wd_cancel(&priv->txtimeout);
+    }
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+static int mpfs_interrupt_1(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-1");
+  return 0;
+}
+
+static int mpfs_interrupt_2(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-2");
+  return 0;
+}
+
+static int mpfs_interrupt_3(int irq, void *context, FAR void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  UNUSED(priv);
+  UNUSED(irq);
+  UNUSED(context);
+
+  ninfo("IRQ-3");
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_txdone
+ *
+ * Description:
+ *   An interrupt was received indicating that one or more frames have
+ *   completed transmission.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   queue - The queue number that completed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct gmac_txdesc_s *txdesc;
+
+  /* Are there any outstanding transmissions?  Loop until either (1) all of
+   * the TX descriptors have been examined, or (2) until we encounter the
+   * first descriptor that is still in use by the hardware.
+   */
+
+  while (priv->queue[queue].txhead != priv->queue[queue].txtail)
+    {
+      /* Yes. check the next buffer at the tail of the list */
+
+      txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail];
+
+      /* Is this TX descriptor done transmitting?
+       * First TX descriptor in chain has GEM_TX_DMA_USED = 1
+       */
+
+      if ((txdesc->status & GEM_TX_DMA_USED) == 0)
+        {
+          break;
+        }
+
+      /* Increment the tail index */
+
+      if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+        {
+          /* Wrap to the beginning of the TX descriptor list */
+
+          priv->queue[queue].txtail = 0;
+        }
+
+      /* At least one TX descriptor is available.  Re-enable RX interrupts.
+       * RX interrupts may previously have been disabled when we ran out of
+       * TX descriptors (see comments in mpfs_transmit()).
+       */
+
+      *priv->queue[queue].int_enable = INT_RX;
+    }
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_recvframe
+ *
+ * Description:
+ *   The function is called when a frame is received. It scans the RX
+ *   descriptors of the received frame and assembles the full packet/
+ *
+ *   NOTE: This function will silently discard any packets containing errors.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *   qi    - Queue index number
+ *
+ * Returned Value:
+ *   OK if a packet was successfully returned; -EAGAIN if there are no
+ *   further packets available
+ *
+ * Assumptions:
+ *   - Global interrupts are disabled by interrupt handling logic.
+ *   - The RX descriptor D-cache list has been invalided to force fetching
+ *     from RAM.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi)
+{
+  volatile struct gmac_rxdesc_s *rxdesc;
+  struct net_driver_s *dev;
+  uintptr_t addr;
+  uint8_t  *dest;
+  uint32_t rxndx;
+  uint32_t pktlen;
+  uint16_t copylen;
+  bool isframe;
+
+  /* Process received RX descriptor.  The ownership bit is set by the GMAC
+   * once it has successfully written a frame to memory.
+   */
+
+  dev        = &priv->dev;
+  dev->d_len = 0;
+  dest       = dev->d_buf;
+  pktlen     = 0;
+  rxndx      = priv->queue[qi].rxndx;
+  rxdesc     = &priv->queue[qi].rx_desc_tab[rxndx];
+  isframe    = false;
+
+  ninfo("rxndx: %" PRId32 "\n", rxndx);
+
+  while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0)
+    {
+      /* The start of frame bit indicates the beginning of a frame.  Discard
+       * any previous fragments.
+       */
+
+      if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0)
+        {
+          /* Skip previous fragments */
+
+          while (rxndx != priv->queue[qi].rxndx)
+            {
+              /* Give ownership back to the GMAC */
+
+              rxdesc = &priv->queue[qi].
+                        rx_desc_tab[priv->queue[qi].rxndx];
+              rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+              /* Increment the RX index */
+
+              if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                {
+                  priv->queue[qi].rxndx = 0;
+                }
+            }
+
+          /* Reset the packet data pointer and packet length */
+
+          dest   = dev->d_buf;
+          pktlen = 0;
+
+          /* Start to gather buffers into the packet buffer */
+
+          isframe = true;
+        }
+
+      /* Increment the working index */
+
+      if (++rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+        {
+          rxndx = 0;
+        }
+
+      /* Copy data into the packet buffer */
+
+      if (isframe)
+        {
+          if (rxndx == priv->queue[qi].rxndx)
+            {
+              nerr("ERROR: No EOF (Invalid or buffers too small)\n");
+              do
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+              while (rxndx != priv->queue[qi].rxndx);
+              return -EIO;
+            }
+
+          /* Get the number of bytes to copy from the buffer */
+
+          copylen = GMAC_RX_UNITSIZE;
+          if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE)
+            {
+              copylen = CONFIG_NET_ETH_PKTSIZE - pktlen;
+            }
+
+          /* And do the copy */
+
+          addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK);
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+          addr += (uintptr_t)(rxdesc->addr_hi) << 32;
+#endif
+          memcpy(dest, (const void *)addr, copylen);
+          dest   += copylen;
+          pktlen += copylen;
+
+          /* If the end of frame has been received, return the data */
+
+          if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0)
+            {
+              /* Frame size from the GMAC */
+
+              dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK;
+              ninfo("packet %d-%" PRId32 " (%d)\n",
+                    priv->queue[qi].rxndx, rxndx, dev->d_len);
+
+              /* All data have been copied in the application frame buffer,
+               * release the RX descriptor
+               */
+
+              while (priv->queue[qi].rxndx != rxndx)
+                {
+                  /* Give ownership back to the GMAC */
+
+                  rxdesc = &priv->queue[qi].
+                            rx_desc_tab[priv->queue[qi].rxndx];
+                  rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+
+                  /* Increment the RX index */
+
+                  if (++priv->queue[qi].rxndx
+                      >= CONFIG_MPFS_ETHMAC_NRXBUFFERS)
+                    {
+                      priv->queue[qi].rxndx = 0;
+                    }
+                }
+
+              /* Check if the device packet buffer was large enough to accept
+               * all of the data.
+               */
+
+              ninfo("rxndx: %d d_len: %d\n",
+                    priv->queue[qi].rxndx, dev->d_len);
+
+              if (pktlen < dev->d_len)
+                {
+                  nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n",
+                       dev->d_len, pktlen);
+                  return -E2BIG;
+                }
+
+              return OK;
+            }
+        }
+
+      /* We have not encountered the SOF yet... discard this fragment
+       * and keep looking
+       */
+
+      else
+        {
+          /* Give ownership back to the GMAC */
+
+          rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER;
+          priv->queue[qi].rxndx = rxndx;
+        }
+
+      /* Process the next buffer */
+
+      rxdesc = &priv->queue[qi].rx_desc_tab[rxndx];
+    }
+
+  /* isframe indicates that we have found a SOF. If we've received a SOF,
+   * but not an EOF in the sequential buffers we own, it must mean that we
+   * have a partial packet. This should only happen if there was a Buffer
+   * Not Available (BNA) error.  When bursts of data come in, quickly
+   * filling the available buffers, before our interrupts can even service
+   * them. Eventually, the ring buffer loops back on itself and the
+   * peripheral sees it cannot write the next fragment of the packet.
+   *
+   * In this case, we keep the rxndx at the start of the last frame, since
+   * the peripheral will finish writing the packet there next.
+   */
+
+  if (!isframe)
+    {
+      priv->queue[qi].rxndx = rxndx;
+    }
+
+  ninfo("rxndx: %d\n", priv->queue[qi].rxndx);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: mpfs_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of onr or more
+ *   new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Loop while while mpfs_recvframe() successfully retrieves valid
+   * GMAC frames.
+   */
+
+  while (mpfs_recvframe(priv, queue) == OK)
+    {
+      mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+      /* Check if the packet is a valid size for the network buffer
+       * configuration (this should not happen)
+       */
+
+      if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
+        {
+          nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
+          continue;
+        }
+
+  #ifdef CONFIG_NET_PKT
+      /* When packet sockets are enabled, feed the frame into the packet
+       * tap
+       */
+
+      pkt_input(&priv->dev);
+  #endif
+
+      /* We only accept IP packets of the configured type and ARP packets */
+
+  #ifdef CONFIG_NET_IPv4
+      if (BUF->type == HTONS(ETHTYPE_IP))
+        {
+          ninfo("IPv4 frame\n");
+
+          /* Handle ARP on input then give the IPv4 packet to the network
+           * layer
+           */
+
+          arp_ipin(&priv->dev);
+          ipv4_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv6
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+                {
+                  arp_out(&priv->dev);
+                }
+  #ifdef CONFIG_NET_IPv6
+              else
+                {
+                  neighbor_out(&priv->dev);
+                }
+  #endif
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_IPv6
+      if (BUF->type == HTONS(ETHTYPE_IP6))
+        {
+          ninfo("IPv6 frame\n");
+
+          /* Give the IPv6 packet to the network layer */
+
+          ipv6_input(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              /* Update the Ethernet header with the correct MAC address */
+
+  #ifdef CONFIG_NET_IPv4
+              if (IFF_IS_IPv4(priv->dev.d_flags))
+                {
+                  arp_out(&priv->dev);
+                }
+              else
+  #endif
+                {
+                  neighbor_out(&priv->dev);
+                }
+
+              /* And send the packet */
+
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+
+  #ifdef CONFIG_NET_ARP
+      if (BUF->type == htons(ETHTYPE_ARP))
+        {
+          ninfo("ARP frame\n");
+
+          /* Handle ARP packet */
+
+          arp_arpin(&priv->dev);
+
+          /* If the above function invocation resulted in data that should be
+           * sent out on the network, the field  d_len will set to a
+           * value > 0.
+           */
+
+          if (priv->dev.d_len > 0)
+            {
+              mpfs_transmit(priv, queue);
+            }
+        }
+      else
+  #endif
+        {
+          nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
+        }
+    }
+
+    ninfo("receive done.\n");
+}
+
+/****************************************************************************
+ * Function: mpfs_interrupt_work
+ *
+ * Description:
+ *   Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_interrupt_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  uint32_t isr;
+  uint32_t rsr;
+  uint32_t tsr;
+  uint32_t regval;
+  uint32_t queue = 0; /* todo: get from arg */
+
+  /* Process pending Ethernet interrupts */
+
+  net_lock();
+  isr = *priv->queue[queue].int_status;
+  rsr = mac_getreg(priv, RECEIVE_STATUS);
+  tsr = mac_getreg(priv, TRANSMIT_STATUS);
+
+  ninfo("isr: %08" PRIx32 "\n", isr);
+
+  /* Check for the completion of a transmission.  This should be done before
+   * checking for received data (because receiving can cause another
+   * transmission before we had a chance to handle the last one).
+   *
+   * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+   * TSR:COMP is set when a frame has been transmitted. Cleared by writing a
+   *   one to this bit.
+   */
+
+  if (tsr != 0)
+    {
+      ninfo("TX tsr=0x%X\n", tsr);
+      uint32_t tx_error = 0;
+
+      /* Check for Retry Limit Exceeded  */
+
+      if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0)
+        {
+          ++tx_error;
+          nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr);
+        }
+
+      /* Check Collision Occurred  */
+
+      if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0)
+        {
+          nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Frame Corruption due to AMBA error */
+
+      if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0)
+        {
+          nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Transmit Underrun (UND)
+       *
+       * ISR:UND is set transmit DMA was not able to read data from memory,
+       *   either because the bus was not granted in time, because a not
+       *   OK hresp(bus error) was returned or because a used bit was read
+       *   midway through frame transmission. If this occurs, the
+       *   transmitter forces bad CRC. Cleared by writing a one to this bit.
+       */
+
+      if ((tsr & TRANSMIT_STATUS_UNDERRUN) != 0)
+        {
+          nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0)
+        {
+          nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Check for Late Collisions */
+
+      if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0)
+        {
+          nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr);
+          ++tx_error;
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, TRANSMIT_STATUS, tsr);
+
+      /* Handle errors */
+
+      if (tx_error != 0)
+        {
+          mpfs_txreset(priv);
+          nerr("TX ERROR: reset\n");
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_TRANSMIT;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+
+      /* And handle the TX done event */
+
+      if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0)
+        {
+          ninfo("TXCOMP: cancel timeout\n");
+          wd_cancel(&priv->txtimeout);
+
+          mpfs_txdone(priv, queue);
+        }
+    }
+
+  /* Check for the receipt of an RX packet.
+   *
+   * RXCOMP indicates that a packet has been received and stored in memory.
+   * RSR:REC indicates that one or more frames have been received and placed
+   *   in memory. This indication is cleared by writing a one to this bit.
+   */
+
+  if (rsr != 0)
+    {
+      uint32_t rx_error = 0;
+      ninfo("RX: rsr=0x%X\n", rsr);
+
+      if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0)
+        {
+          /* Handle the received packet */
+
+          mpfs_receive(priv, queue);
+        }
+
+      /* Check for Receive Overrun */
+
+      if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for buffer not available (BNA) */
+
+      if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Check for HRESP not OK */
+
+      if ((rsr &  RECEIVE_STATUS_RESP_NOT_OK) != 0)
+        {
+          ++rx_error;
+          nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr);
+        }
+
+      /* Clear status */
+
+      mac_putreg(priv, RECEIVE_STATUS, rsr);
+
+      if (rx_error != 0)
+        {
+          nerr("RX ERROR: reset\n");
+          mpfs_rxreset(priv);
+          *priv->queue[queue].int_status = 0xffffffff;
+          mac_putreg(priv, RECEIVE_STATUS, 0xffffffff);
+
+          /* rxreset disables reveiver so re-enable it */
+
+          regval = mac_getreg(priv, NETWORK_CONTROL);
+          regval |= NETWORK_CONTROL_ENABLE_RECEIVE;
+          mac_putreg(priv, NETWORK_CONTROL, regval);
+        }
+    }
+
+  net_unlock();
+
+  /* Re-enable Ethernet interrupts */
+
+  regval = INT_ALL;
+  *priv->queue[queue].int_enable = regval;
+}
+
+/****************************************************************************
+ * Function: mpfs_txreset
+ *
+ * Description:
+ *  Reset the transmit logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_txreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *txbuffer;
+  struct gmac_txdesc_s *txdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable TX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Configure the TX descriptors. */
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      txbuffer = priv->queue[qi].txbuffer;
+      txdesc   = priv->queue[qi].tx_desc_tab;
+      priv->queue[qi].txhead = 0;
+      priv->queue[qi].txtail = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE];
+
+          /* Set the buffer address and mark the descriptor as in used by
+           * firmware.
+           */
+
+          txdesc[ndx].addr    = lower_32_bits(bufaddr);
+          txdesc[ndx].status  = GEM_TX_DMA_USED;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          txdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED |
+                                                         GEM_TX_DMA_WRAP;
+
+      /* Set the Transmit Buffer Queue Base Register and Disable queue */
+
+      *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u;
+    }
+
+  /* REVISIT: for now enable only queue 0 for DMA operation */
+
+  *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].tx_desc_tab);
+}
+
+/****************************************************************************
+ * Function: mpfs_rxreset
+ *
+ * Description:
+ *  Reset the receive logic
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_rxreset(struct mpfs_ethmac_s *priv)
+{
+  uint8_t *rxbuffer;
+  struct gmac_rxdesc_s *rxdesc;
+  uintptr_t bufaddr;
+  uint32_t regval;
+  int qi;
+  int ndx;
+
+  /* Disable RX */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* first set the common upper32-bits of 64 bit address */
+
+  bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr));
+
+  /* Configure the RX descriptors. */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      rxbuffer = priv->queue[qi].rxbuffer;
+      rxdesc   = priv->queue[qi].rx_desc_tab;
+      priv->queue[qi].rxndx = 0;
+
+      for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++)
+        {
+          bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE];
+
+          /* Set the buffer address and remove GMACRXD_ADDR_OWNER and
+           * GMACRXD_ADDR_WRAP.
+           */
+
+          rxdesc[ndx].addr    = lower_32_bits(bufaddr);
+          rxdesc[ndx].status  = 0;
+#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE
+          rxdesc[ndx].addr_hi = upper_32_bits(bufaddr);
+#endif
+        }
+
+      /* Mark the final descriptor in the list */
+
+      rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP;
+
+      /* Set the Receive Buffer Queue Base Register and disable queue */
+
+      *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u;
+    }
+
+  /* REVISIT: enable only queue 0 for DMA operation */
+
+  *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t)
+                                           priv->queue[0].rx_desc_tab);
+  *priv->queue[0].int_status = 0xffffffff;
+}
+
+/****************************************************************************
+ * Function: mpfs_txinuse
+ *
+ * Description:
+ *   Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  uint32_t txhead32 = priv->queue[queue].txhead;
+  if (priv->queue[queue].txtail > txhead32)
+    {
+      txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS;
+    }
+
+  return (uint16_t)(txhead32 - priv->queue[queue].txtail);
+}
+
+/****************************************************************************
+ * Function: mpfs_txfree
+ *
+ * Description:
+ *   Return the number of TX buffers available
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  /* The number available is equal to the total number of buffers, minus the
+   * number of buffers in use.  Notice that that actual number of buffers is
+   * the configured size minus 1.
+   */
+
+  return (CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue);
+}
+
+/****************************************************************************
+ * Function: mpfs_macaddress
+ *
+ * Description:
+ *   Configure the selected MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_macaddress(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+  uint32_t regval;
+
+  ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        dev->d_ifname,
+        dev->d_mac.ether.ether_addr_octet[0],
+        dev->d_mac.ether.ether_addr_octet[1],
+        dev->d_mac.ether.ether_addr_octet[2],
+        dev->d_mac.ether.ether_addr_octet[3],
+        dev->d_mac.ether.ether_addr_octet[4],
+        dev->d_mac.ether.ether_addr_octet[5]);
+
+  /* Set the MAC address */
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24;
+  mac_putreg(priv, SPEC_ADD1_BOTTOM, regval);
+
+  regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] |
+           (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8;
+  mac_putreg(priv, SPEC_ADD1_TOP, regval);
+}
+
+/****************************************************************************
+ * Function: mpfa_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_txpoll(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      /* Look up the destination MAC address and add it to the Ethernet
+       * header.
+       */
+
+  #ifdef CONFIG_NET_IPv4
+  #ifdef CONFIG_NET_IPv6
+      if (IFF_IS_IPv4(priv->dev.d_flags))
+  #endif
+        {
+          arp_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv4 */
+
+  #ifdef CONFIG_NET_IPv6
+    #ifdef CONFIG_NET_IPv4
+      else
+  #endif
+        {
+          neighbor_out(&priv->dev);
+        }
+  #endif /* CONFIG_NET_IPv6 */
+
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          mpfs_transmit(priv, 0);
+
+          /* Check if there are any free TX descriptors.  We cannot perform
+           * the TX poll if we do not have buffering for another packet.
+           */
+
+          if (mpfs_txfree(priv, 0) == 0)
+            {
+              /* We have to terminate the poll if we have no more descriptors
+               * available for another transfer.
+               */
+
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: mpfs_dopoll
+ *
+ * Description:
+ *   The function is called in order to perform an out-of-sequence TX poll.
+ *   This is done:
+ *
+ *   1. After completion of a transmission (mpfs_txdone),
+ *   2. When new TX data is available (mpfs_txavail), and
+ *   3. After a TX timeout to restart the sending process
+ *      (mpfs_txtimeout_expiry).
+ *
+ * Input Parameters:
+ *   priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_dopoll(struct mpfs_ethmac_s *priv)
+{
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* If we have the descriptor,
+       * then poll the network for new XMIT data.
+       */
+
+      devif_timer(dev, 0, mpfs_txpoll);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_expiry
+ *
+ * Description:
+ *   Periodic timer handler.  Called from the timer interrupt handler.
+ *
+ * Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  /* Schedule to perform the interrupt processing on the worker thread. */
+
+  if (work_available(&priv->pollwork))
+    {
+      work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0);
+    }
+  else
+    {
+      wd_start(&priv->txpoll, MPFS_WDDELAY,
+               mpfs_poll_expiry, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Function: mpfs_poll_work
+ *
+ * Description:
+ *   Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_poll_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  struct net_driver_s *dev = &priv->dev;
+
+  /* Check if there are any free TX descriptors.  We cannot perform the
+   * TX poll if we do not have buffering for another packet.
+   */
+
+  net_lock();
+  if (mpfs_txfree(priv, 0) > 0)
+    {
+      /* Update TCP timing states and poll the network for new XMIT data. */
+
+      devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll);
+    }
+
+  /* Setup the watchdog poll timer again */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifup(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  int ret;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %d.%d.%d.%d\n",
+        (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
+        (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+  /* Configure the Ethernet interface for DMA operation. */
+
+  ret = mpfs_ethconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Set the MAC address (should have been configured while we were down) */
+
+  mpfs_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  mpfs_ipv6multicast(priv);
+#endif
+
+  /* Initialize for PHY access */
+
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyinit failed: %d\n", ret);
+      return ret;
+    }
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+
+  ret = mpfs_autonegotiate(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret);
+      return ret;
+    }
+#else
+  /* Just force the configured link speed */
+
+  mpfs_linkspeed(priv);
+#endif
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+
+  /* Set and activate a timer process */
+
+  wd_start(&priv->txpoll, MPFS_WDDELAY,
+           mpfs_poll_expiry, (wdparm_t)priv);
+
+  /* Enable the Ethernet interrupts */
+
+  priv->ifup = true;
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ifdown(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  irqstate_t flags;
+
+  ninfo("Taking the network down\n");
+
+  /* Disable the Ethernet interrupt */
+
+  flags = enter_critical_section();
+  up_disable_irq(priv->mac_q_int[0]);
+  up_disable_irq(priv->mac_q_int[1]);
+  up_disable_irq(priv->mac_q_int[2]);
+  up_disable_irq(priv->mac_q_int[3]);
+
+  *priv->queue[0].int_disable = 0xffffffff;
+  *priv->queue[1].int_disable = 0xffffffff;
+  *priv->queue[2].int_disable = 0xffffffff;
+  *priv->queue[3].int_disable = 0xffffffff;
+
+  /* Cancel the TX poll timer and TX timeout timers */
+
+  wd_cancel(&priv->txpoll);
+  wd_cancel(&priv->txtimeout);
+
+  /* Put the MAC in its reset, non-operational state.  This should be
+   * a known configuration that will guarantee the mpfs_ifup() always
+   * successfully brings the interface back up.
+   */
+
+  mpfs_ethreset(priv);
+
+  /* Mark the device "down" */
+
+  priv->ifup = false;
+  leave_critical_section(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ *   arg  - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void mpfs_txavail_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  ninfo("ifup: %d\n", priv->ifup);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->ifup)
+    {
+      /* Poll the network for new XMIT data */
+
+      mpfs_dopoll(priv);
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int mpfs_txavail(struct net_driver_s *dev)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_hashindx
+ *
+ * Description:
+ *   Cacuclate the hash address register index.  The hash address register
+ *   is 64 bits long and takes up two locations in the memory map. The
+ *   destination address is reduced to a 6-bit index into the 64-bit Hash
+ *   Register using the following hash function: The hash function is an XOR
+ *   of every sixth bit of the destination address.
+ *
+ *   ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ *   ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ *   ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ *   ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ *   ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ *   ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ *   Where da:00 represents the least significant bit of the first byte
+ *   received and da:47 represents the most significant bit of the last byte
+ *   received.
+ *
+ * Input Parameters:
+ *   mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ *   The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int mpfs_hashindx(const uint8_t *mac)
+{
+  unsigned int ndx;
+
+  ndx = mac[0];
+  ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+  ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+  ndx ^= (mac[2] >> 2);
+  ndx ^= mac[3];
+  ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+  ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+  ndx ^= (mac[5] >> 2);
+
+  return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: mpfs_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regoffset;
+  uint32_t regval;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Add the multicast address to the hardware multicast hash table */
+
+  if (ndx >= 32)
+    {
+      regoffset = HASH_TOP;         /* Hash Register Top [63:32] Register */
+      bit       = 1 << (ndx - 32);  /* Bit 0-31 */
+    }
+  else
+    {
+      regoffset = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit       = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval = mac_getreg(priv, regoffset);
+  regval |= bit;
+  mac_putreg(priv, regoffset, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE;
+  regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+  uint32_t regval;
+  uint32_t regaddr1;
+  uint32_t regaddr2;
+  unsigned int ndx;
+  unsigned int bit;
+  UNUSED(priv);
+
+  ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+  /* Calculate the 6-bit has table index */
+
+  ndx = mpfs_hashindx(mac);
+
+  /* Remove the multicast address to the hardware multicast hast table */
+
+  if (ndx >= 32)
+    {
+      regaddr1 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      regaddr2 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      bit      = 1 << (ndx - 32); /* Bit 0-31 */
+    }
+  else
+    {
+      regaddr1 = HASH_BOTTOM;     /* Hash Register Bottom [31:0] Register */
+      regaddr2 = HASH_TOP;        /* Hash Register Top [63:32] Register */
+      bit      = 1 << ndx;        /* Bit 0-31 */
+    }
+
+  regval  = mac_getreg(priv, regaddr1);
+  regval &= ~bit;
+  mac_putreg(priv, regaddr1, regval);
+
+  /* The unicast hash enable and the multicast hash enable bits in the
+   * Network Configuration Register enable the reception of hash matched
+   * frames:
+   *
+   * - A multicast match will be signalled if the multicast hash enable bit
+   *   is set, da:00 is logic 1 and the hash index points to a bit set in
+   *   the Hash Register.
+   * - A unicast match will be signalled if the unicast hash enable bit is
+   *   set, da:00 is logic 0 and the hash index points to a bit set in the
+   *   Hash Register.
+   */
+
+  /* Are all multicast address matches disabled? */
+
+  if (regval == 0 && mac_getreg(priv, regaddr2) == 0)
+    {
+      /* Yes.. disable all address matching */
+
+      regval  = mac_getreg(priv, NETWORK_CONFIG);
+      regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE |
+                  NETWORK_CONFIG_MULTICAST_HASH_ENABLE);
+      mac_putreg(priv, NETWORK_CONFIG, regval);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_enablemdio
+ *
+ * Description:
+ *  Enable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_enablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Enable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  regval |= NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_disablemdio
+ *
+ * Description:
+ *  Disable the management port
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_disablemdio(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t enables;
+
+  /* Disable management port */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE |
+                      NETWORK_CONTROL_ENABLE_TRANSMIT);
+
+  regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval &= ~NETWORK_CONTROL_MAN_PORT_EN;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  regval |= enables;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyread
+ *
+ * Description:
+ *  Read a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                        uint8_t regaddr, uint16_t *phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_READ |
+           PHY_MANAGEMENT_WRITE_1;
+
+  mac_putreg(priv, PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Return the PHY data */
+
+  *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) &
+            PHY_MANAGEMENT_PHY_DATA_MASK);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywrite
+ *
+ * Description:
+ *  Write to a PHY register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *   phyaddr - The PHY device address
+ *   regaddr - The PHY register address
+ *   phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr,
+                         uint8_t regaddr, uint16_t phyval)
+{
+  uint32_t regval;
+  int ret;
+
+  /* Make sure that the PHY is idle */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Write the PHY Maintenance register */
+
+  regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 |
+           PHY_MANAGEMENT_REG_ADDRESS(regaddr) |
+           PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) |
+           PHY_MANAGEMENT_OPERATION_WRITE |
+           PHY_MANAGEMENT_WRITE_1;
+  mac_putreg(priv,  PHY_MANAGEMENT, regval);
+
+  /* Wait until the PHY is again IDLE */
+
+  ret = mpfs_phywait(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywait failed: %d\n", ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phywait
+ *
+ * Description:
+ *  Wait for the PHY to become IDLE
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phywait(struct mpfs_ethmac_s *priv)
+{
+  unsigned int retries;
+
+  /* Loop for the configured number of attempts */
+
+  for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+    {
+      /* Is the PHY IDLE */
+
+      if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyfind
+ *
+ * Description:
+ *  Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr)
+{
+  uint16_t phyval;
+  uint8_t candidate;
+  unsigned int offset;
+  int ret = -ESRCH;
+
+  ninfo("Find a valid PHY address\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+#ifdef CONFIG_MPFS_HAVE_CORERMII
+  ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS);
+
+  ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR,
+                      CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX |
+                      CORE_RMII_100MBIT);
+  if (ret != OK)
+    {
+      nerr("Core RMII reset write failed!\n");
+    }
+
+  ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval);
+  ninfo("CORE-RMII after reset MCR=%d\n", phyval);
+  if ((phyval != 0x03) || (ret != OK))
+    {
+      nerr("Core RMII reset read failed! val=%d\n", phyval);
+    }
+#endif
+
+  /* Check initial candidate address */
+
+  candidate = *phyaddr;
+
+  ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+  if (ret == OK && phyval != 0xffff)
+    {
+      *phyaddr = candidate;
+      ret = OK;
+    }
+
+  /* The current address does not work... try another */
+
+  else
+    {
+      nerr("ERROR: mpfs_phyread failed for PHY address %02x: %d\n",
+           candidate, ret);
+
+      for (offset = 0; offset < 32; offset++)
+        {
+          /* Get the next candidate PHY address */
+
+          candidate = (candidate + 1) & 0x1f;
+
+          /* Try reading the PHY ID from the candidate PHY address */
+
+          ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval);
+          if (ret == OK && phyval != 0xffff)
+            {
+              ret = OK;
+              break;
+            }
+        }
+    }
+
+  if (ret == OK)
+    {
+      ninfo("  PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+      *phyaddr = candidate;
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_phydump
+ *
+ * Description:
+ *   Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void mpfs_phydump(struct mpfs_ethmac_s *priv)
+{
+  uint16_t phyval;
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  ninfo("GMII Registers (Address %02x)\n", priv->phyaddr);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  ninfo("       MCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+  ninfo("       MSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval);
+  ninfo(" ADVERTISE: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval);
+  ninfo("       LPR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval);
+  ninfo("  1000BTCR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval);
+  ninfo("  1000BTSR: %04x\n", phyval);
+  mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval);
+  ninfo("   ESTATUS: %04x\n", phyval);
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_autonegotiate
+ *
+ * Description:
+ *  Autonegotiate speed and duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MPFS_MAC_AUTONEG
+static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t control;
+  uint32_t linkmode;
+  uint16_t phyval;
+  uint16_t phyid1;
+  uint16_t phyid2;
+  uint16_t advertise;
+  uint16_t lpa;
+  int timeout;
+  int ret;
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  uint16_t btsr;
+  uint16_t btcr;
+#endif
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Read the MSB bits of the OUI from the PHYID1 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID1 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+  /* Read the LS bits of the OUI from the PHYID2 register */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read PHYID2 register\n");
+      goto errout;
+    }
+
+  ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+  if ((phyid1 != 0xffff) && (phyid2 != 0xffff))
+    {
+      ninfo("  Vendor Model Number:   %04x\n",
+            (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT);
+      ninfo("  Model Revision Number: %04x\n",
+            (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT);
+    }
+
+  /* Set the Auto_negotiation Advertisement Register, MII advertising for
+   * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3
+   */
+
+  advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF |
+              GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF |
+              GMII_ADVERTISE_8023;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write ADVERTISE register\n");
+      goto errout;
+    }
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+  /* Modify the 1000Base-T control register to advertise 1000Base-T full
+   * and half duplex support.
+   */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+  btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret);
+      goto errout;
+    }
+
+#endif
+
+  /* Restart Auto_negotiation */
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  phyval |= GMII_MCR_ANRESTART;
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to write MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to read MCR register: %d\n", ret);
+      goto errout;
+    }
+
+  ninfo(" MCR: 0x%X\n", phyval);
+
+  /* Wait for autonegotiation to complete */
+
+  timeout = 0;
+  for (; ; )
+    {
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read MSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Check for completion of autonegotiation */
+
+      if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0)
+        {
+          /* Yes break out of the loop */
+
+          ninfo("AutoNegotiate complete\n");
+          break;
+        }
+
+      /* No check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Setup the GMAC local link speed */
+
+  linkmode = 0;  /* 10Base-T Half-Duplex */
+  timeout  = 0;
+
+  for (; ; )
+    {
+  #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS
+      ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 &&
+          (btcr & GMII_1000BTCR_1000BASETHALF) != 0)
+        {
+          /* Set RGMII for 1000BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 1000\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX |
+                     NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+      else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 &&
+               (btcr & GMII_1000BTCR_1000BASETFULL) != 0)
+        {
+          /* Set RGMII for 1000BaseT and Half Duplex */
+
+          ninfo("Link: HD - 1000\n");
+          linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+          break;
+        }
+  #endif
+
+      /* Get the Autonegotiation Link partner base page */
+
+      ret  = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa);
+      if (ret < 0)
+        {
+          nerr("ERROR: Failed to read LPA register: %d\n", ret);
+          goto errout;
+        }
+
+      /* Setup the GMAC link speed */
+
+      if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 &&
+          (lpa & GMII_LPA_100BASETXFULL) != 0)
+        {
+          /* Set RGMII for 100BaseTX and Full Duplex */
+
+          ninfo("Link: FD - 100\n");
+          linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX);
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 &&
+               (lpa & GMII_LPA_10BASETXFULL) != 0)
+        {
+          /* Set RGMII for 10BaseT and Full Duplex */
+
+          ninfo("Link: FD - 10\n");
+          linkmode = NETWORK_CONFIG_FULL_DUPLEX;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_100BASETXHALF) != 0)
+        {
+          /* Set RGMII for 100BaseTX and half Duplex */
+
+          ninfo("Link: HD - 100\n");
+          linkmode = NETWORK_CONFIG_SPEED;
+          break;
+        }
+      else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 &&
+               (lpa & GMII_LPA_10BASETXHALF) != 0)
+        {
+          /* Set RGMII for 10BaseT and half Duplex */
+
+          ninfo("Link: HD - 10\n");
+          break;
+        }
+
+      /* Check for a timeout */
+
+      if (++timeout >= PHY_RETRY_MAX)
+        {
+          nerr("ERROR: TimeOut\n");
+          mpfs_phydump(priv);
+          ret = -ETIMEDOUT;
+          goto errout;
+        }
+    }
+
+  /* Disable RX and TX momentarily */
+
+  control = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             control & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                         NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the negotiated
+   * speed and duplex
+   */
+
+  regval  = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+  regval |= linkmode;
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, control);
+
+errout:
+
+  /* Disable the management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_linkspeed
+ *
+ * Description:
+ *  If autonegotiation is not configured, then just force the configuration
+ *  mode
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_MPFS_MAC_AUTONEG
+static void mpfs_linkspeed(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+  uint32_t ncr;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL,
+             ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+                     NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NETWORK_CONFIG register based on the configured
+   * speed and duplex
+   */
+
+  regval = mac_getreg(priv, NETWORK_CONFIG);
+  regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX |
+              NETWORK_CONFIG_GIGABIT_MODE_ENABLE);
+
+#ifdef CONFIG_MPFS_MAC_ETHFD
+  regval |= NETWORK_CONFIG_FULL_DUPLEX;
+#endif
+
+#if defined(CONFIG_MPFS_MAC_ETH100MBPS)
+  regval |= NETWORK_CONFIG_SPEED;
+#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS)
+  regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE;
+#endif
+
+  ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval);
+
+  mac_putreg(priv, NETWORK_CONFIG, regval);
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+#endif
+
+/****************************************************************************
+ * Function: mpfs_ioctl
+ *
+ * Description:
+ *  Handles driver ioctl calls:
+ *
+ *  SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ *    events.
+ *
+ *  SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ *    Executes the SIOCxMIIxxx command and responds using the request struct
+ *    that must be provided as its 2nd parameter.
+ *
+ *    When called with SIOCGMIIPHY it will get the PHY address for the device
+ *    and write it to the req->phy_id field of the request struct.
+ *
+ *    When called with SIOCGMIIREG it will read a register of the PHY that is
+ *    specified using the req->reg_no struct field and then write its output
+ *    to the req->val_out field.
+ *
+ *    When called with SIOCSMIIREG it will write to a register of the PHY
+ *    that is specified using the req->reg_no struct field and use
+ *    req->val_in as its input.
+ *
+ * Input Parameters:
+ *   dev - Ethernet device structure
+ *   cmd - SIOCxMIIxxx command code
+ *   arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private;
+#endif
+  int ret;
+
+  switch (cmd)
+    {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+        {
+          struct mii_ioctl_notify_s *req =
+                  (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+          ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+          if (ret == OK)
+            {
+              /* Enable PHY link up/down interrupts */
+
+              ret = mpfs_phyintenable(priv);
+            }
+        }
+        break;
+#endif
+
+      case SIOCGMIIPHY: /* Get MII PHY address */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+          req->phy_id = priv->phyaddr;
+          ret = OK;
+        }
+        break;
+
+      case SIOCGMIIREG: /* Get register from MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Read from the requested register */
+
+          ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+
+      case SIOCSMIIREG: /* Set register in MII PHY */
+        {
+          struct mii_ioctl_data_s *req =
+                  (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+          /* Enable the management port */
+
+          mpfs_enablemdio(priv);
+
+          /* Write to the requested register */
+
+          ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+          /* Disable the management port */
+
+          mpfs_disablemdio(priv);
+        }
+        break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: mpfs_buffer_initialize
+ *
+ * Description:
+ *   Allocate aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv,
+                                  unsigned int queue)
+{
+#ifdef CONFIG_MPFS_ETHMAC_PREALLOCATE
+  /* Use pre-allocated buffers */
+
+  priv->txdesc   = g_txdesc;
+  priv->rxdesc   = g_rxdesc;
+  priv->txbuffer = g_txbuffer;
+  priv->rxbuffer = g_rxbuffer;
+
+#else
+  size_t allocsize;
+
+  /* Allocate buffers */
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s);
+  priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *)
+                                   kmm_memalign(8, allocsize);
+  if (priv->queue[queue].tx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX descriptors\n");
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].tx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s);
+  priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rx_desc_tab == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX descriptors\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  memset(priv->queue[queue].rx_desc_tab, 0, allocsize);
+
+  allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE;
+  priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].txbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate TX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+  allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE;
+  priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+  if (priv->queue[queue].rxbuffer == NULL)
+    {
+      nerr("ERROR: Failed to allocate RX buffer\n");
+      mpfs_buffer_free(priv, queue);
+      return -ENOMEM;
+    }
+
+#endif
+
+  DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].rxbuffer    & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 &&
+              ((uintptr_t)priv->queue[queue].txbuffer    & 7) == 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_buffer_free
+ *
+ * Description:
+ *   Free aligned TX and RX descriptors and buffers.  For the case of
+ *   pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ *   priv - The GMAC driver state
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+#ifndef CONFIG_MPFS_GMAC_PREALLOCATE
+  /* Free allocated buffers */
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].rx_desc_tab != NULL)
+    {
+      kmm_free(priv->queue[queue].rx_desc_tab);
+      priv->queue[queue].rx_desc_tab = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+
+  if (priv->queue[queue].txbuffer != NULL)
+    {
+      kmm_free(priv->queue[queue].txbuffer);
+      priv->queue[queue].txbuffer = NULL;
+    }
+#endif
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_work
+ *
+ * Description:
+ *   Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ *   arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ *   OK on success
+ *
+ * Assumptions:
+ *   Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_work(void *arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+
+  nerr("ERROR: TX-Timeout!\n");
+
+  /* Reset the hardware.  Just take the interface down, then back up again. */
+
+  net_lock();
+  mpfs_ifdown(&priv->dev);
+  mpfs_ifup(&priv->dev);
+
+  /* Then poll the network for new XMIT data */
+
+  mpfs_dopoll(priv);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: mpfs_txtimeout_expiry
+ *
+ * Description:
+ *   Our TX watchdog timed out.  Called from the timer interrupt handler.
+ *   The last TX never completed.  Reset the hardware and start again.
+ *
+ * Input Parameters:
+ *   arg  - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void mpfs_txtimeout_expiry(wdparm_t arg)
+{
+  struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg;
+  unsigned int qi;
+
+  /* Disable further Ethernet interrupts.  This will prevent some race
+   * conditions with interrupt work.  There is still a potential race
+   * condition with interrupt work that is already queued and in progress.
+   */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+    }
+
+  /* Schedule to perform the TX timeout processing on the worker thread. */
+
+  work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: mpfs_transmit
+ *
+ * Description:
+ *   Start hardware transmission.  Called either from the TX done interrupt
+ *   handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *  queue  - The queue to send from
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue)
+{
+  struct net_driver_s *dev = &priv->dev;
+  volatile struct gmac_txdesc_s *txdesc;
+  uintptr_t addr;
+  uint32_t status;
+  uint32_t regval;
+
+  ninfo("d_len: %d txhead: %d txtail: %d\n",
+        dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail);
+  mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+  /* Check parameter */
+
+  if (dev->d_len > GMAC_TX_UNITSIZE)
+    {
+      nerr("ERROR: Packet too big: %d\n", dev->d_len);
+      return -EINVAL;
+    }
+
+  /* Pointer to the current TX descriptor */
+
+  txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead];
+
+  /* If no free TX descriptor, buffer can't be sent */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      nerr("ERROR: No free TX descriptors\n");
+      return -EBUSY;
+    }
+
+  /* Mark buffer as used temporarily so DMA doesn't operate on it */
+
+  status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST;
+  txdesc->status = status;
+
+  /* Setup/Copy data to transmission buffer */
+
+  if (dev->d_len > 0)
+    {
+      addr = txdesc->addr;
+#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE
+      addr += (uintptr_t)(txdesc->addr_hi) << 32;
+#endif
+      memcpy((void *)addr, dev->d_buf, dev->d_len);
+    }
+
+  /* Update TX descriptor status. */
+
+  status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST;
+
+  if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1)
+    {
+      status |= GEM_TX_DMA_WRAP;
+    }
+
+  /* Update the descriptor status */
+
+  txdesc->status = status;
+
+  /* Increment the head index */
+
+  if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS)
+    {
+      priv->queue[queue].txhead = 0;
+    }
+
+  /* Now start transmission (if it is not already done) */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= NETWORK_CONTROL_TRANSMIT_START;
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* Set up the TX timeout watchdog (perhaps restarting the timer) */
+
+  wd_start(&priv->txtimeout, MPFS_TXTIMEOUT,
+           mpfs_txtimeout_expiry, (wdparm_t)priv);
+
+  /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+   * available.
+   */
+
+  dev->d_len = 0;
+
+  /* If we have no more available TX descriptors, then we must disable the
+   * RCOMP interrupt to stop further RX processing.  Why?  Because EACH RX
+   * packet that is dispatched is also an opportunity to reply with a TX
+   * packet.  So, if we cannot handle an RX packet reply, then we disable
+   * all RX packet processing.
+   */
+
+  if (mpfs_txfree(priv, queue) < 1)
+    {
+      ninfo("Disabling RX interrupts\n");
+      *priv->queue[queue].int_disable = INT_RX;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethreset
+ *
+ * Description:
+ *  Reset the Ethernet block.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void mpfs_ethreset(struct mpfs_ethmac_s *priv)
+{
+  int qi;
+
+  /* if we are supporting PHY IOCTLs, then do not reset the MAC. */
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+  if (priv->regbase == MPFS_GEM0_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC0);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC0, 0);
+    }
+
+  if (priv->regbase == MPFS_GEM1_LO_BASE)
+    {
+      /* reset */
+
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  0, SYSREG_SOFT_RESET_CR_MAC1);
+      modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET,
+                  SYSREG_SOFT_RESET_CR_MAC1, 0);
+    }
+
+#endif
+
+  /* Disable all GMAC interrupts */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *priv->queue[qi].int_disable = 0xffffffff;
+      *priv->queue[qi].int_status  = 0xffffffff;
+    }
+
+  /* Reset RX and TX logic */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+}
+
+/****************************************************************************
+ * Function: mpfs_macconfig
+ *
+ * Description:
+ *  Configure the Ethernet MAC for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macconfig(struct mpfs_ethmac_s *priv)
+{
+  uint32_t net_config = 0U;
+  uint32_t net_control = 0U;
+  uint32_t dma_config = 0U;
+  uintptr_t addr;
+  unsigned int qi;
+
+  net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS;
+
+  net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) |
+               ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK)
+                  << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT;
+#endif
+
+#ifdef CONFIG_NET_LOOPBACK
+  net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL;
+#endif
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES;
+#endif
+
+#ifdef CONFIG_MPFS_MAC_NO_BROADCAST
+  net_config |= NETWORK_CONFIG_NO_BROADCAST;
+#endif
+
+  mac_putreg(priv, NETWORK_CONTROL, net_control);
+  mac_putreg(priv, NETWORK_CONFIG,  net_config);
+
+  mac_putreg(priv, RECEIVE_STATUS,  0xffffffff);
+  mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff);
+
+  /* Disable and clear all ints */
+
+  for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].int_disable) = ((uint32_t)0xfffffffful);
+      *(priv->queue[qi].int_status)  = ((uint32_t)0xfffffffful);
+    }
+
+  /* TODO: If using only queue0 use all memory for that.
+   * TX_Q_SEG_ALLOC_Q_LOWER xxx
+   */
+
+#ifdef CONFIG_MPFS_MAC_SGMII
+  /* Reset PCS */
+
+  mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET);
+#endif
+
+  /* Configure MAC Network DMA Config register */
+
+  dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) |
+                DMA_CONFIG_TX_PBUF_SIZE |
+                (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) |
+                (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK));
+
+#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE)
+  dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1;
+#endif
+
+  mac_putreg(priv, DMA_CONFIG, dma_config);
+
+  for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++)
+    {
+      *(priv->queue[qi].dma_rxbuf_size) = ((uint32_t)MPFS_MAC_RX_BUF_VALUE);
+    }
+
+  /* Disable the other queues as the GEM reset leaves them enabled with an
+   * address pointer of 0. Setting b0 of the queue pointer disables a queue.
+   */
+
+  addr = (uintptr_t)priv->queue[0].tx_desc_tab;
+  mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr));
+  addr = (uintptr_t)priv->queue[0].rx_desc_tab;
+  mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr));
+
+  mac_putreg(priv, TRANSMIT_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, TRANSMIT_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q1_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q2_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+  mac_putreg(priv, RECEIVE_Q3_PTR,
+              ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_macenable
+ *
+ * Description:
+ *  Enable normal MAC operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_macenable(struct mpfs_ethmac_s *priv)
+{
+  uint32_t regval;
+
+  /* Reset TX and RX */
+
+  mpfs_rxreset(priv);
+  mpfs_txreset(priv);
+
+  /* enable queue-0 DMA */
+
+  uint32_t r = *priv->queue[0].rx_q_ptr & ~1u;
+  *priv->queue[0].rx_q_ptr = r;
+
+  /* enable global irqs */
+
+  up_enable_irq(priv->mac_q_int[0]);
+  up_enable_irq(priv->mac_q_int[1]);
+  up_enable_irq(priv->mac_q_int[2]);
+  up_enable_irq(priv->mac_q_int[3]);
+
+  /* Enable Rx and Tx, enable the statistics registers. */
+
+  regval  = mac_getreg(priv, NETWORK_CONTROL);
+  regval |= (NETWORK_CONTROL_ENABLE_RECEIVE |
+             NETWORK_CONTROL_ENABLE_TRANSMIT |
+             NETWORK_CONTROL_STATS_WRITE_EN);
+  mac_putreg(priv, NETWORK_CONTROL, regval);
+
+  /* enable queue-0 irqs */
+
+  *priv->queue[0].int_status = 0xffffffff;
+  *priv->queue[0].int_enable = INT_ALL;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_mdcclock
+ *
+ * Description:
+ *  Configure the MDC clocking
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void mpfs_mdcclock(struct mpfs_ethmac_s *priv)
+{
+  uint32_t ncfgr;
+  uint32_t ncr;
+  uint32_t mck;
+
+  /* Disable RX and TX momentarily */
+
+  ncr = mac_getreg(priv, NETWORK_CONTROL);
+  mac_putreg(priv, NETWORK_CONTROL, ncr &
+             ~(NETWORK_CONTROL_ENABLE_RECEIVE |
+              NETWORK_CONTROL_ENABLE_TRANSMIT));
+
+  /* Modify the NCFGR register based on the configured board MCK frequency */
+
+  ncfgr  = mac_getreg(priv, NETWORK_CONFIG);
+  ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK <<
+             NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT);
+
+  mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ;
+  DEBUGASSERT(mck <= 240000000);
+
+  if (mck <= 20000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8;
+    }
+  else if (mck <= 40000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16;
+    }
+  else if (mck <= 80000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32;
+    }
+  else if (mck <= 120000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48;
+    }
+  else if (mck <= 160000000)
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64;
+    }
+  else
+    {
+      ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96;
+    }
+
+  mac_putreg(priv, NETWORK_CONFIG, ncfgr);
+
+  /* Restore RX and TX enable settings */
+
+  mac_putreg(priv, NETWORK_CONTROL, ncr);
+}
+
+/****************************************************************************
+ * Function: mpfs_phyinit
+ *
+ * Description:
+ *  Configure the PHY and determine the link speed/duplex.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_phyinit(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  /* Configure PHY clocking */
+
+  mpfs_mdcclock(priv);
+
+  /* Check the PHY Address */
+
+  priv->phyaddr = CONFIG_MPFS_PHYADDR;
+  ret = mpfs_phyfind(priv, &priv->phyaddr);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phyfind failed: %d\n", ret);
+      return ret;
+    }
+
+  /* We have a PHY address.  Reset the PHY */
+
+  mpfs_phyreset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Function: mpfs_phyreset
+ *
+ * Description:
+ *  Reset the PHY
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_phyreset(struct mpfs_ethmac_s *priv)
+{
+  uint16_t mcr;
+  int timeout;
+  int ret;
+
+  ninfo(" mpfs_phyreset\n");
+
+  /* Enable management port */
+
+  mpfs_enablemdio(priv);
+
+  /* Reset the PHY */
+
+  ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR,
+                      GMII_MCR_RESET);
+  if (ret < 0)
+    {
+      nerr("ERROR: mpfs_phywrite failed: %d\n", ret);
+    }
+
+  /* Wait for the PHY reset to complete */
+
+  ret = -ETIMEDOUT;
+  for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++)
+    {
+      mcr = GMII_MCR_RESET;
+      int result = mpfs_phyread(priv, priv->phyaddr,
+                                GMII_MCR, &mcr);
+      if (result < 0)
+        {
+          nerr("ERROR: Failed to read the MCR register: %d\n", ret);
+          ret = result;
+        }
+      else if ((mcr & GMII_MCR_RESET) == 0)
+        {
+          ret = OK;
+          break;
+        }
+    }
+
+  /* Disable management port */
+
+  mpfs_disablemdio(priv);
+  return ret;
+}
+
+/****************************************************************************
+ * Function: mpfs_ethconfig
+ *
+ * Description:
+ *  Configure the Ethernet interface for DMA operation.
+ *
+ * Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int mpfs_ethconfig(struct mpfs_ethmac_s *priv)
+{
+  int ret;
+
+  ninfo("Entry\n");
+
+#ifdef CONFIG_MPFS_PHYINIT
+  /* Perform any necessary, board-specific PHY initialization */
+
+  ret = mpfs_phy_boardinitialize(0);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  /* Reset the Ethernet block */
+
+  ninfo("Reset the Ethernet block\n");
+  mpfs_ethreset(priv);
+
+  /* Initialize the PHY */
+
+  ninfo("Initialize the PHY\n");
+  ret = mpfs_phyinit(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Initialize the MAC and DMA */
+
+  ninfo("Initialize the MAC and DMA\n");
+  ret = mpfs_macconfig(priv);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Enable normal MAC operation */
+
+  ninfo("Enable normal operation\n");
+  return mpfs_macenable(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: mpfs_ethinitialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver for one interface.  If the STM32 chip
+ *   supports multiple Ethernet controllers, then board specific logic
+ *   must implement arm_netinitialize() and call this function to initialize
+ *   the desired interfaces.
+ *
+ * Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which GMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_LATEINIT)
+int mpfs_ethinitialize(int intf)
+#else
+static inline int mpfs_ethinitialize(int intf)
+#endif

Review comment:
       Lets got with `int mpfs_ethinitialize(int intf);` only and remove `static` case




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org