This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new c5a3089e87 drivers/net: Add support for the NCV7410 10BASE-T1S MAC-PHY
c5a3089e87 is described below

commit c5a3089e87792228be37ac03cd195fc91cb90fd6
Author: michal matias <mich4l.mat...@gmail.com>
AuthorDate: Wed Jun 25 02:21:00 2025 +0200

    drivers/net: Add support for the NCV7410 10BASE-T1S MAC-PHY
    
    This commit adds driver for the Onsemi NCV7410 10BASE-T1S
    Ethernet MAC-PHY.
    
    Signed-off-by: michal matias <mich4l.mat...@gmail.com>
---
 drivers/net/CMakeLists.txt  |    4 +
 drivers/net/Kconfig         |   24 +
 drivers/net/Make.defs       |    4 +
 drivers/net/ncv7410.c       | 1662 +++++++++++++++++++++++++++++++++++++++++++
 drivers/net/ncv7410.h       |  291 ++++++++
 include/nuttx/net/ncv7410.h |   88 +++
 6 files changed, 2073 insertions(+)

diff --git a/drivers/net/CMakeLists.txt b/drivers/net/CMakeLists.txt
index 86c96da39d..3d0937f7f5 100644
--- a/drivers/net/CMakeLists.txt
+++ b/drivers/net/CMakeLists.txt
@@ -49,6 +49,10 @@ if(CONFIG_NET)
     list(APPEND SRCS enc28j60.c)
   endif()
 
+  if(CONFIG_NCV7410)
+    list(APPEND SRCS ncv7410.c)
+  endif()
+
   if(CONFIG_ENCX24J600)
     list(APPEND SRCS encx24j600.c)
   endif()
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index dee800c9db..5b7c1841c8 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -287,6 +287,30 @@ config ENC28J60_REGDEBUG
 
 endif # ENC28J60
 
+menuconfig NCV7410
+       bool "onsemi NCV7410 support"
+       default n
+       select SPI
+       select ARCH_HAVE_NETDEV_STATISTICS
+       ---help---
+               Enable support for onsemi NCV7410 10BASE-T1S MAC-PHY
+
+if NCV7410
+
+config NCV7410_INT_PIN
+       int "NCV7410 interrupt pin"
+       default 3
+       ---help---
+               Number of boarad pin to be connected to NCV7410's interrupt 
signal
+
+config NCV7410_FREQUENCY
+       int "SPI frequency"
+       default 20000000
+       ---help---
+               NCV7410 SPI bus frequency
+
+endif # NCV7410
+
 menuconfig ENCX24J600
        bool "Microchip ENCX24J600 support"
        default n
diff --git a/drivers/net/Make.defs b/drivers/net/Make.defs
index 20a2b2b138..f341749752 100644
--- a/drivers/net/Make.defs
+++ b/drivers/net/Make.defs
@@ -50,6 +50,10 @@ ifeq ($(CONFIG_ENC28J60),y)
   CSRCS += enc28j60.c
 endif
 
+ifeq ($(CONFIG_NCV7410),y)
+  CSRCS += ncv7410.c
+endif
+
 ifeq ($(CONFIG_ENCX24J600),y)
   CSRCS += encx24j600.c
 endif
diff --git a/drivers/net/ncv7410.c b/drivers/net/ncv7410.c
new file mode 100644
index 0000000000..d21647c885
--- /dev/null
+++ b/drivers/net/ncv7410.c
@@ -0,0 +1,1662 @@
+/****************************************************************************
+ * drivers/net/ncv7410.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/spi/spi.h>
+#include <sys/endian.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/signal.h>
+#include <nuttx/mutex.h>
+#include <nuttx/net/ncv7410.h>
+#include <nuttx/net/netdev_lowerhalf.h>
+
+#include "ncv7410.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define NCVWORK LPWORK
+
+#define NCV_RESET_TRIES 5
+
+/* Maximum frame size = (MTU + LL heaader size) + FCS size */
+
+#define NCV_MAX_FRAME_SIZE(p) (p->dev.netdev.d_pktsize + 4)
+
+/* Packet Memory ************************************************************/
+
+/* Maximum number of allocated tx and rx packets */
+
+#define NCV7410_TX_QUOTA        1
+#define NCV7410_RX_QUOTA        2
+
+#if CONFIG_IOB_NBUFFERS < (NCV7410_TX_QUOTA + NCV7410_RX_QUOTA)
+#  error "CONFIG_IOB_NBUFFERS must be > (NCV7410_TX_QUOTA + NCV7410_RX_QUOTA)"
+#endif
+
+#ifndef CONFIG_SCHED_LPWORK
+#  error "CONFIG_SCHED_LPWORK is needed by NCV7410 driver"
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The ncv7410_driver_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+enum ncv_ifstate_e
+{
+  NCV_RESET,
+  NCV_INIT_DOWN,
+  NCV_INIT_UP
+};
+
+struct ncv7410_driver_s
+{
+  struct netdev_lowerhalf_s dev;   /* Driver data visible by the net stack
+                                    * (must be placed first)                */
+  mutex_t lock;                    /* Lock for data race prevention         */
+  FAR struct spi_dev_s *spi;       /* The SPI device instance               */
+  int irqnum;                      /* irq number of the interrupt pin       */
+  struct ncv7410_config_s *config; /* NCV7410 configuration                 */
+  uint8_t ifstate;                 /* Driver state from ncv_ifstate_e enum  */
+
+  struct work_s interrupt_work;    /* wq handle for the interrupt work      */
+  struct work_s io_work;           /* wq handle for the io work             */
+
+  int txc;                         /* TX credits                            */
+  int rca;                         /* RX chunks available                   */
+
+  FAR netpkt_t *tx_pkt;            /* Pointer to the TX netpacket           */
+  FAR netpkt_t *rx_pkt;            /* Pointer to the RX netpacket           */
+  int tx_pkt_idx;                  /* Position in the TX netpacket          */
+  int rx_pkt_idx;                  /* Position in the RX netpacket          */
+  int tx_pkt_len;                  /* Length of the TX packet               */
+  bool rx_pkt_ready;               /* RX packet ready to be received flag   */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Bit calculations */
+
+static int ncv_get_parity(uint32_t word);
+static uint8_t ncv_bitrev8(uint8_t byte);
+
+/* SPI transfers */
+
+static int ncv_write_reg(FAR struct ncv7410_driver_s *priv,
+                         oa_regid_t regid, uint32_t word);
+
+static int ncv_read_reg(FAR struct ncv7410_driver_s *priv,
+                        oa_regid_t regid, FAR uint32_t *word);
+
+static int ncv_set_clear_bits(FAR struct ncv7410_driver_s *priv,
+                              oa_regid_t regid,
+                              uint32_t setbits, uint32_t clearbits);
+
+static int ncv_poll_footer(FAR struct ncv7410_driver_s *priv,
+                           FAR uint32_t *footer);
+
+static int ncv_exchange_chunk(FAR struct ncv7410_driver_s *priv,
+                              FAR uint8_t *txbuf, FAR uint8_t *rxbuf,
+                              uint32_t header, uint32_t *footer);
+
+/* Interrupt handling */
+
+static int ncv_interrupt(int irq, FAR void *context, FAR void *arg);
+static void ncv_interrupt_work(FAR void *arg);
+
+/* Data Transaction Protocol logic */
+
+static void ncv_io_work(FAR void *arg);
+static uint32_t ncv_prepare_chunk_exchange(FAR struct ncv7410_driver_s *priv,
+                                           FAR uint8_t *txbuf);
+static bool ncv_can_rx(FAR struct ncv7410_driver_s *priv);
+static void ncv_try_finish_tx_packet(FAR struct ncv7410_driver_s *priv);
+static void ncv_handle_rx_chunk(FAR struct ncv7410_driver_s *priv,
+                                uint32_t footer, FAR uint8_t *rxbuf);
+static void ncv_finalize_rx_packet(FAR struct ncv7410_driver_s *priv);
+static void ncv_release_tx_packet(FAR struct ncv7410_driver_s *priv);
+static void ncv_release_rx_packet(FAR struct ncv7410_driver_s *priv);
+
+/* SPI inline utility functions */
+
+static inline void ncv_select_spi(FAR struct ncv7410_driver_s *priv);
+static inline void ncv_deselect_spi(FAR struct ncv7410_driver_s *priv);
+
+/* ncv7410 reset and configuration */
+
+static int ncv_reset(FAR struct ncv7410_driver_s *priv);
+static int ncv_config(FAR struct ncv7410_driver_s *priv);
+static int ncv_enable(FAR struct ncv7410_driver_s *priv);
+static int ncv_disable(FAR struct ncv7410_driver_s *priv);
+static int ncv_init_mac_addr(FAR struct ncv7410_driver_s *priv);
+
+/* Driver buffer manipulation */
+
+static void ncv_reset_driver_buffers(FAR struct ncv7410_driver_s *priv);
+
+/* NuttX callback functions */
+
+static int ncv7410_ifup(FAR struct netdev_lowerhalf_s *dev);
+static int ncv7410_ifdown(FAR struct netdev_lowerhalf_s *dev);
+static int ncv7410_transmit(FAR struct netdev_lowerhalf_s *dev,
+                            FAR netpkt_t *pkt);
+static FAR netpkt_t *ncv7410_receive(FAR struct netdev_lowerhalf_s *dev);
+
+/* Debug */
+
+#ifdef CONFIG_DEBUG_NET_INFO
+static void ncv_print_footer(uint32_t footer);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct netdev_ops_s g_ncv7410_ops =
+{
+  .ifup     = ncv7410_ifup,
+  .ifdown   = ncv7410_ifdown,
+  .transmit = ncv7410_transmit,
+  .receive  = ncv7410_receive,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ncv_interrupt
+ *
+ * Description:
+ *   Schedule interrupt work when the interrupt signal from MAC-PHY is
+ *   received.
+ *
+ * Input Parameters:
+ *   irq     - not used
+ *   context - not used
+ *   arg     - ncv7410_driver_s priv structure to be passed to the interrupt
+ *             worker
+ *
+ * Returned Value:
+ *   OK is always returned.
+ *
+ ****************************************************************************/
+
+static int ncv_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+  FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)arg;
+
+  ninfo("NCV7410 interrupt!\n");
+
+  /* schedule interrupt work */
+
+  work_queue(NCVWORK, &priv->interrupt_work, ncv_interrupt_work, priv, 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv_interrupt_work
+ *
+ * Description:
+ *   Identify the interrupt source and perform necessary work.
+ *
+ * Input Parameters:
+ *   arg - pointer to driver private data
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void ncv_interrupt_work(FAR void *arg)
+{
+  FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)arg;
+  uint32_t footer;
+
+  nxmutex_lock(&priv->lock);
+
+  if (priv->ifstate != NCV_INIT_UP)
+    {
+      nxmutex_unlock(&priv->lock);
+      return;
+    }
+
+  ninfo("NCV7410 interrupt worker invoked!\n");
+
+  /* poll the data chunk footer */
+
+  if (ncv_poll_footer(priv, &footer))
+    {
+      nerr("Polling footer unsuccessful\n");
+
+      /* TODO: don't */
+
+      PANIC();
+    }
+
+#ifdef CONFIG_DEBUG_NET_INFO
+  ncv_print_footer(footer);
+#endif
+
+  /* if EXST in the footer, check enabled sources
+   * STATUS0, link-status in clause 22 phy registers
+   * (not yet implemented)
+   */
+
+  /* update MAC-PHY buffer status */
+
+  priv->txc = oa_tx_credits(footer);
+  priv->rca = oa_rx_available(footer);
+
+  if ((priv->tx_pkt && priv->txc) || priv->rca)
+    {
+      /* schedule IO work */
+
+      work_queue(NCVWORK, &priv->io_work, ncv_io_work, priv, 0);
+    }
+
+  nxmutex_unlock(&priv->lock);
+}
+
+/****************************************************************************
+ * Name: ncv_io_work
+ *
+ * Description:
+ *   Exchange data chunk with the MAC-PHY.
+ *
+ * Input Parameters:
+ *   arg - pointer to driver private data
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void ncv_io_work(FAR void *arg)
+{
+  FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)arg;
+
+  uint8_t txbuf[NCV_CHUNK_DEFAULT_PAYLOAD_SIZE];
+  uint8_t rxbuf[NCV_CHUNK_DEFAULT_PAYLOAD_SIZE];
+
+  uint32_t header;
+  uint32_t footer;
+
+  nxmutex_lock(&priv->lock);
+
+  if (priv->ifstate != NCV_INIT_UP)
+    {
+      nxmutex_unlock(&priv->lock);
+      return;
+    }
+
+  header = ncv_prepare_chunk_exchange(priv, txbuf);
+
+  /* Perform the SPI exchange */
+
+  if (ncv_exchange_chunk(priv, txbuf, rxbuf, header, &footer))
+    {
+      nerr("Error during chunk exchange\n");
+
+      /* TODO: do not panic, the best is probably to report the error
+       * and reset MAC to some defined state and reset driver
+       */
+
+      PANIC();
+    }
+
+  ncv_try_finish_tx_packet(priv);
+
+  ncv_handle_rx_chunk(priv, footer, rxbuf);
+
+  /* schedule further work if needed */
+
+  if ((priv->tx_pkt && priv->txc) || priv->rca)
+    {
+      work_queue(NCVWORK, &priv->io_work, ncv_io_work, priv, 0);
+    }
+
+  nxmutex_unlock(&priv->lock);
+}
+
+/****************************************************************************
+ * Name: ncv_prepare_chunk_exchange
+ *
+ * Description:
+ *   Determine whether there is data to transmit or receive.
+ *   Set the appropriate header bitfields and fill the txbuf accordingly.
+ *
+ * Input Parameters:
+ *   priv  - pointer to the driver-specific state structure
+ *   txbuf - pointer to the transmit chunk buffer
+ *
+ * Returned Value:
+ *   Returns the prepared chunk header.
+ *
+ ****************************************************************************/
+
+static uint32_t ncv_prepare_chunk_exchange(FAR struct ncv7410_driver_s *priv,
+                                           FAR uint8_t *txbuf)
+{
+  uint32_t header = 0;
+  int txlen;
+
+  if (priv->tx_pkt && priv->txc)
+    {
+      header |= (1 << OA_DV_POS);  /* Data Valid */
+
+      if (priv->tx_pkt_idx == 0)
+        {
+          header |=   (1 << OA_SV_POS)   /* Start Valid */
+                    | (0 << OA_SWO_POS); /* Start Word Offset = 0 */
+        }
+
+      txlen = priv->tx_pkt_len - priv->tx_pkt_idx;
+
+      if (txlen <= NCV_CHUNK_DEFAULT_PAYLOAD_SIZE)
+        {
+          header |=   (1 << OA_EV_POS)             /* End Valid */
+                    | ((txlen - 1) << OA_EBO_POS); /* End Byte Offset */
+        }
+      else
+        {
+          txlen = NCV_CHUNK_DEFAULT_PAYLOAD_SIZE;
+        }
+
+      /* copy data from network to txbuf */
+
+      netpkt_copyout(&priv->dev, txbuf, priv->tx_pkt,
+                     txlen, priv->tx_pkt_idx);
+      priv->tx_pkt_idx += txlen;
+    }
+
+  if (ncv_can_rx(priv) == false)
+    {
+      header |= (1 << OA_NORX_POS);  /* no rx */
+    }
+
+  return header;
+}
+
+/****************************************************************************
+ * Name: ncv_can_rx
+ *
+ * Description:
+ *   Determine whether rx data is available and whether it can be received.
+ *
+ * Input Parameters:
+ *   priv - pointer to the driver-specific state structure
+ *
+ * Returned Value:
+ *   If it is possible to receive an rx chunk, true is returned,
+ *   otherwise false is returned.
+ *
+ ****************************************************************************/
+
+static bool ncv_can_rx(FAR struct ncv7410_driver_s *priv)
+{
+  if (!priv->rca)
+    {
+      return false;
+    }
+
+  if (priv->rx_pkt_ready)
+    {
+      return false;
+    }
+
+  if (priv->rx_pkt)
+    {
+      return true;
+    }
+
+  /* no RX packet, try to alloc */
+
+  priv->rx_pkt = netpkt_alloc(&priv->dev, NETPKT_RX);
+  if (priv->rx_pkt)
+    {
+      return true;
+    }
+
+  ninfo("INFO: Failed to alloc rx netpkt\n");
+
+  /* there is no buffer for rx data */
+
+  return false;
+}
+
+/****************************************************************************
+ * Name: ncv_try_finish_tx_packet
+ *
+ * Description:
+ *   Check whether the entire packet has been transmitted.
+ *   If so, free the tx netpkt and notify the upperhalf.
+ *
+ * Input Parameters:
+ *   priv - pointer to the driver-specific state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void ncv_try_finish_tx_packet(FAR struct ncv7410_driver_s *priv)
+{
+  if (priv->tx_pkt && (priv->tx_pkt_idx == priv->tx_pkt_len))
+    {
+      ncv_release_tx_packet(priv);
+      netdev_lower_txdone(&priv->dev);
+    }
+}
+
+/****************************************************************************
+ * Name: ncv_handle_rx_chunk
+ *
+ * Description:
+ *   Parse the received footer, update buffer status and handle data
+ *   in the rxbuf.
+ *
+ * Input Parameters:
+ *   priv   - pointer to the driver-specific state structure
+ *   footer - the received footer
+ *   rxbuf  - pointer to the received data buffer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void ncv_handle_rx_chunk(FAR struct ncv7410_driver_s *priv,
+                                uint32_t footer, FAR uint8_t *rxbuf)
+{
+  int rxlen;
+  int newlen;
+
+  /* update buffer status */
+
+  priv->txc = oa_tx_credits(footer);
+  priv->rca = oa_rx_available(footer);
+
+  /* check rx_pkt && !rx_pkt_ready,
+   * oa_data_valid flag might have been set due to an SPI error
+   */
+
+  if (oa_data_valid(footer) && priv->rx_pkt && !priv->rx_pkt_ready)
+    {
+      if (oa_start_valid(footer))
+        {
+          priv->rx_pkt_idx = 0;
+        }
+
+      if (oa_end_valid(footer))
+        {
+          if (oa_frame_drop(footer))
+            {
+              ncv_release_rx_packet(priv);
+              return;
+            }
+
+          rxlen = oa_end_byte_offset(footer) + 1;
+        }
+      else
+        {
+          rxlen = NCV_CHUNK_DEFAULT_PAYLOAD_SIZE;
+        }
+
+      newlen = priv->rx_pkt_idx + rxlen;
+
+      if (newlen > NCV_MAX_FRAME_SIZE(priv))
+        {
+          nwarn("Dropping chunk of a packet that is too long");
+
+          /* set index so that a subsequent chunk with
+           * smaller payload won't pass
+           */
+
+          priv->rx_pkt_idx = NCV_MAX_FRAME_SIZE(priv) + 1;
+          return;
+        }
+
+      netpkt_copyin(&priv->dev, priv->rx_pkt, rxbuf,
+                    rxlen, priv->rx_pkt_idx);
+      priv->rx_pkt_idx = newlen;
+
+      if (oa_end_valid(footer))
+        {
+          /* finalize packet and notify the upper */
+
+          ncv_finalize_rx_packet(priv);
+          netdev_lower_rxready(&priv->dev);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: ncv_finalize_rx_packet
+ *
+ * Description:
+ *   Strip down last 4 bytes (FCS) from the rx packet and mark it ready.
+ *
+ * Input Parameters:
+ *   priv - pointer to the driver-specific state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void ncv_finalize_rx_packet(FAR struct ncv7410_driver_s *priv)
+{
+  netpkt_setdatalen(&priv->dev, priv->rx_pkt,
+                    netpkt_getdatalen(&priv->dev, priv->rx_pkt) - 4);
+  priv->rx_pkt_ready = true;
+}
+
+/****************************************************************************
+ * Name: ncv_release_tx_packet
+ *
+ * Description:
+ *   Release the tx packet.
+ *
+ * Input Parameters:
+ *   priv - pointer to the driver-specific state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void ncv_release_tx_packet(FAR struct ncv7410_driver_s *priv)
+{
+  netpkt_free(&priv->dev, priv->tx_pkt, NETPKT_TX);
+  priv->tx_pkt = NULL;
+}
+
+/****************************************************************************
+ * Name: ncv_release_rx_packet
+ *
+ * Description:
+ *   Release the rx packet.
+ *
+ * Input Parameters:
+ *   priv - pointer to the driver-specific state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void ncv_release_rx_packet(FAR struct ncv7410_driver_s *priv)
+{
+  netpkt_free(&priv->dev, priv->rx_pkt, NETPKT_RX);
+  priv->rx_pkt = NULL;
+}
+
+/****************************************************************************
+ * Name: ncv_get_parity
+ *
+ * Description:
+ *   Obtain parity of a 32-bit word.
+ *
+ * Input Parameters:
+ *   word - 32-bit word, subject to the parity calculation
+ *
+ * Returned Value:
+ *   If the parity of the word is even, zero is returned.
+ *   Otherwise one is returned.
+ *
+ ****************************************************************************/
+
+static int ncv_get_parity(uint32_t word)
+{
+  /* www-graphics.stanford.edu/~seander/bithacks.html */
+
+  word ^= word >> 1;
+  word ^= word >> 2;
+  word = (word & 0x11111111u) * 0x11111111u;
+  return (word >> 28) & 1;
+}
+
+/****************************************************************************
+ * Name: ncv_bitrev8
+ *
+ * Description:
+ *   Perform a bit reverse of a byte.
+ *
+ * Input Parameters:
+ *   b - byte to be reversed
+ *
+ * Returned Value:
+ *   Byte with reversed bits is returned.
+ *
+ ****************************************************************************/
+
+static uint8_t ncv_bitrev8(uint8_t byte)
+{
+  /* https://stackoverflow.com/a/2602885 */
+
+  byte = (byte & 0xf0) >> 4 | (byte & 0x0f) << 4;
+  byte = (byte & 0xcc) >> 2 | (byte & 0x33) << 2;
+  byte = (byte & 0xaa) >> 1 | (byte & 0x55) << 1;
+  return byte;
+}
+
+/****************************************************************************
+ * Name: ncv_(select/deselect)_spi
+ *
+ * Description:
+ *   Helper functions to setup SPI hardware.
+ *
+ * Input Parameters:
+ *   priv - pointer to the driver-specific state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static inline void ncv_select_spi(FAR struct ncv7410_driver_s *priv)
+{
+  SPI_LOCK(priv->spi, true);
+
+  SPI_SETMODE(priv->spi, OA_SPI_MODE);
+  SPI_SETBITS(priv->spi, OA_SPI_NBITS);
+  SPI_HWFEATURES(priv->spi, 0);  /* disable HW features */
+  SPI_SETFREQUENCY(priv->spi, CONFIG_NCV7410_FREQUENCY);
+
+  SPI_SELECT(priv->spi, priv->config->id, true);
+}
+
+static inline void ncv_deselect_spi(FAR struct ncv7410_driver_s *priv)
+{
+  SPI_SELECT(priv->spi, priv->config->id, false);
+
+  SPI_LOCK(priv->spi, false);
+}
+
+/****************************************************************************
+ * Name: ncv_write_reg
+ *
+ * Description:
+ *   Write to a MAC-PHY register.
+ *
+ * Input Parameters:
+ *   priv  - pointer to the driver-specific state structure
+ *   regid - Register id encapsulating MMS and ADDR
+ *   word  - 32-bit word to be written to the register
+ *
+ * Returned Value:
+ *   On a successful transaction OK is returned, otherwise ERROR is returned.
+ *
+ ****************************************************************************/
+
+static int ncv_write_reg(FAR struct ncv7410_driver_s *priv,
+                         oa_regid_t regid, uint32_t word)
+{
+  uint32_t txdata[3];
+  uint32_t rxdata[3];
+  uint8_t  mms  = OA_REGID_GET_MMS(regid);
+  uint16_t addr = OA_REGID_GET_ADDR(regid);
+
+  /* prepare header */
+
+  uint32_t header =   (1    << OA_WNR_POS)   /* Write Not Read */
+                    | (mms  << OA_MMS_POS)
+                    | (addr << OA_ADDR_POS);
+  int parity = ncv_get_parity(header);
+  header |= parity ? 0 : OA_P_MASK;  /* make header odd parity */
+
+  /* convert to big endian */
+
+  header = htobe32(header);
+  word = htobe32(word);
+
+  /* prepare exchange */
+
+  txdata[0] = header;
+  txdata[1] = word;
+
+  ncv_select_spi(priv);
+  SPI_EXCHANGE(priv->spi, txdata, rxdata, 12);
+  ncv_deselect_spi(priv);
+  if (rxdata[1] != header)
+    {
+      nerr("Error writing register\n");
+      return ERROR;
+    }
+
+  ninfo("Writing register OK\n");
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv_read_reg
+ *
+ * Description:
+ *   Read a MAC-PHY register.
+ *
+ * Input Parameters:
+ *   priv  - pointer to the driver-specific state structure
+ *   regid - register id encapsulating MMS and ADDR
+ *   word  - pointer to a 32-bit destination variable
+ *
+ * Returned Value:
+ *   On successful transaction OK is returned, otherwise ERROR is returned.
+ *
+ ****************************************************************************/
+
+static int ncv_read_reg(FAR struct ncv7410_driver_s *priv,
+                        oa_regid_t regid, FAR uint32_t *word)
+{
+  uint32_t txdata[3];
+  uint32_t rxdata[3];
+  uint8_t  mms  = OA_REGID_GET_MMS(regid);
+  uint16_t addr = OA_REGID_GET_ADDR(regid);
+  int parity;
+  uint32_t header;
+
+  /* prepare header */
+
+  header =   (mms  << OA_MMS_POS)
+           | (addr << OA_ADDR_POS);
+  parity = ncv_get_parity(header);
+  header |= parity ? 0 : OA_P_MASK;  /* make header odd parity */
+
+  /* convert to big endian */
+
+  header = htobe32(header);
+
+  /* prepare exchange */
+
+  txdata[0] = header;
+
+  ncv_select_spi(priv);
+  SPI_EXCHANGE(priv->spi, txdata, rxdata, 12);
+  ncv_deselect_spi(priv);
+
+  *word = be32toh(rxdata[2]);
+  if (rxdata[1] != header)
+    {
+      nerr("Error reading register\n");
+      return ERROR;
+    }
+
+  ninfo("Reading register OK\n");
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv_set_clear_bits
+ *
+ * Description:
+ *   Perform a read-modify-write operation on a given register
+ *   while setting bits from the setbits argument and clearing bits from
+ *   the clearbits argument.
+ *
+ * Input Parameters:
+ *   priv      - pointer to the driver-specific state structure
+ *   regid     - register id of the register to be modified
+ *   setbits   - bits set to one will be set in the register
+ *   clearbits - bits set to one will be cleared in the register
+ *
+ * Returned Value:
+ *   On a successful transaction OK is returned, otherwise ERROR is returned.
+ *
+ ****************************************************************************/
+
+static int ncv_set_clear_bits(FAR struct ncv7410_driver_s *priv,
+                              oa_regid_t regid,
+                              uint32_t setbits, uint32_t clearbits)
+{
+  uint32_t regval;
+
+  if (ncv_read_reg(priv, regid, &regval))
+    {
+      return ERROR;
+    }
+
+  regval |= setbits;
+  regval &= ~clearbits;
+
+  if (ncv_write_reg(priv, regid, regval))
+    {
+      return ERROR;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv_exchange_chunk
+ *
+ * Description:
+ *   Send a data chunk to MAC-PHY and simultaneously receive chunk.
+ *
+ *   Computing header parity, checking footer parity, converting to proper
+ *   endianness and setting DNC flag is done by this function.
+ *
+ * Input Parameters:
+ *   priv   - pointer to the driver-specific state structure
+ *   txbuf  - buffer with transmit chunk data
+ *   rxbuf  - buffer to save the received chunk to
+ *   header - header controlling the transaction
+ *   footer - pointer to a 32-bit value for the footer
+ *
+ * Returned Value:
+ *   On a successful transaction OK is returned, otherwise ERROR is returned.
+ *
+ ****************************************************************************/
+
+static int ncv_exchange_chunk(FAR struct ncv7410_driver_s *priv,
+                              FAR uint8_t *txbuf, FAR uint8_t *rxbuf,
+                              uint32_t header, uint32_t *footer)
+{
+  header |= (1 << OA_DNC_POS);
+  header |= (!ncv_get_parity(header) << OA_P_POS);
+  header = htobe32(header);
+
+  ncv_select_spi(priv);
+
+  /* this depends on SW Chip Select */
+
+  SPI_EXCHANGE(priv->spi, (uint8_t *) &header, rxbuf, 4);
+  SPI_EXCHANGE(priv->spi, txbuf,
+               &rxbuf[4], NCV_CHUNK_DEFAULT_PAYLOAD_SIZE - 4);
+  SPI_EXCHANGE(priv->spi, &txbuf[NCV_CHUNK_DEFAULT_PAYLOAD_SIZE - 4],
+               (uint8_t *)footer, 4);
+  ncv_deselect_spi(priv);
+
+  *footer = be32toh(*footer);
+  if (!ncv_get_parity(*footer))
+    {
+      nerr("Wrong parity in the footer\n");
+      return ERROR;
+    }
+
+  if (oa_header_bad(*footer))
+    {
+      nerr("HDRB set in the footer\n");
+      return ERROR;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv_poll_footer
+ *
+ * Description:
+ *   Poll a data transaction chunk footer.
+ *
+ * Input Parameters:
+ *   priv   - pointer to the driver-specific state structure
+ *   footer - pointer to a 32-bit footer destination variable
+ *
+ * Returned Value:
+ *   On a successful transaction OK is returned, otherwise ERROR is returned.
+ *
+ ****************************************************************************/
+
+static int ncv_poll_footer(FAR struct ncv7410_driver_s *priv,
+                           FAR uint32_t *footer)
+{
+  uint8_t txdata[NCV_CHUNK_DEFAULT_PAYLOAD_SIZE];
+  uint8_t rxdata[NCV_CHUNK_DEFAULT_PAYLOAD_SIZE];
+  uint32_t header;
+
+  header =   (1 << OA_DNC_POS)   /* Data Not Control */
+           | (1 << OA_NORX_POS); /* No Read */
+
+  if (ncv_exchange_chunk(priv, txdata, rxdata, header, footer))
+    {
+      return ERROR;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv_reset
+ *
+ * Description:
+ *   Perform SW reset of the MAC-PHY.
+ *
+ * Input Parameters:
+ *   priv - pointer to the driver-specific state structure
+ *
+ * Returned Value:
+ *   On a successful reset OK is returned, otherwise ERROR is returned.
+ *
+ ****************************************************************************/
+
+static int ncv_reset(FAR struct ncv7410_driver_s *priv)
+{
+  int tries = NCV_RESET_TRIES;
+  uint32_t regval = (1 << OA_RESET_SWRESET_POS);
+
+  if (ncv_write_reg(priv, OA_RESET_REGID, regval))
+    {
+      return ERROR;
+    }
+
+  /* check whether the RESET bit cleared itself */
+
+  do
+    {
+      if (ncv_read_reg(priv, OA_RESET_REGID, &regval))
+        {
+          return ERROR;
+        }
+    }
+  while (tries-- && (regval & OA_RESET_SWRESET_MASK));
+
+  if (regval & OA_RESET_SWRESET_MASK)
+    {
+      return ERROR;
+    }
+
+  /* check whether the reset complete flag is set */
+
+  tries = NCV_RESET_TRIES;
+
+  do
+    {
+      if (ncv_read_reg(priv, OA_STATUS0_REGID, &regval))
+        {
+          return ERROR;
+        }
+    }
+  while (tries-- && !(regval & OA_STATUS0_RESETC_MASK));
+
+  if (!(regval & OA_STATUS0_RESETC_MASK))
+    {
+      return ERROR;
+    }
+
+  /* clear HDRE in STATUS0 (due to a bug in NCV7410) */
+
+  if (ncv_write_reg(priv, OA_STATUS0_REGID, (1 << OA_STATUS0_HDRE_POS)))
+    {
+      return ERROR;
+    }
+
+  /* clear reset complete flag */
+
+  if (ncv_write_reg(priv, OA_STATUS0_REGID, (1 << OA_STATUS0_RESETC_POS)))
+    {
+      return ERROR;
+    }
+
+  /* blink with LEDs for debugging purposes */
+
+  for (int i = 0; i < 4; i++)
+    {
+      regval = 0x0302;
+      if (ncv_write_reg(priv, NCV_DIO_CONFIG_REGID, regval))
+        {
+          return ERROR;
+        }
+
+      nxsig_usleep(250000);
+      regval = 0x0203;
+      if (ncv_write_reg(priv, NCV_DIO_CONFIG_REGID, regval))
+        {
+          return ERROR;
+        }
+
+      nxsig_usleep(250000);
+    }
+
+  /* set DIOs to default */
+
+  regval = NCV_DIO_CONFIG_DEF;
+  if (ncv_write_reg(priv, NCV_DIO_CONFIG_REGID, regval))
+    {
+      return ERROR;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv_config
+ *
+ * Description:
+ *   Configure the MAC-PHY into promiscuous mode and set the SYNC flag.
+ *
+ * Input Parameters:
+ *   priv - pointer to the driver-specific state structure
+ *
+ * Returned Value:
+ *   On success OK is returned, otherwise ERROR is returned.
+ *
+ * Assumptions:
+ *   The function is called after the MAC address is initialized.
+ *
+ ****************************************************************************/
+
+static int ncv_config(FAR struct ncv7410_driver_s *priv)
+{
+  uint32_t regval;
+#ifndef CONFIG_NET_PROMISCUOUS
+  uint8_t *mac = priv->dev.netdev.d_mac.ether.ether_addr_octet;
+#endif
+
+  ninfo("Configuring NCV7410\n");
+
+  /* setup LEDs DIO0: txrx blink
+   *            DIO1: link enabled and link status up
+   */
+
+  regval =   (NCV_DIO_TXRX_FUNC << NCV_DIO0_FUNC_POS)
+           | (NCV_DIO_LINK_CTRL_FUNC << NCV_DIO1_FUNC_POS)
+           | (1 << NCV_DIO0_OUT_VAL_POS)
+           | (1 << NCV_DIO1_OUT_VAL_POS);
+
+  if (ncv_write_reg(priv, NCV_DIO_CONFIG_REGID, regval))
+    {
+      return ERROR;
+    }
+
+  /* enable MAC TX, RX, enable transmit FCS computation on MAC,
+   * enable MAC address filtering
+   */
+
+  regval =   (1 << NCV_MAC_CONTROL0_FCSA_POS)
+           | (1 << NCV_MAC_CONTROL0_TXEN_POS)
+           | (1 << NCV_MAC_CONTROL0_RXEN_POS)
+           | (1 << NCV_MAC_CONTROL0_ADRF_POS);
+
+#ifdef CONFIG_NET_PROMISCUOUS
+  /* disable MAC address filtering */
+
+  regval &= ~(1 << NCV_MAC_CONTROL0_ADRF_POS);
+#endif
+
+  if (ncv_write_reg(priv, NCV_MAC_CONTROL0_REGID, regval))
+    {
+      return ERROR;
+    }
+
+#ifndef CONFIG_NET_PROMISCUOUS
+  /* setup MAC address filter */
+
+  regval =   (mac[2] << 24)
+           | (mac[3] << 16)
+           | (mac[4] << 8)
+           | (mac[5]);
+
+  if (ncv_write_reg(priv, NCV_ADDRFILT0L_REGID, regval))
+    {
+      return ERROR;
+    }
+
+  regval =   (1 << 31)  /* enable filter */
+           | (mac[0] << 8)
+           | (mac[1]);
+
+  if (ncv_write_reg(priv, NCV_ADDRFILT0H_REGID, regval))
+    {
+      return ERROR;
+    }
+
+  regval = 0xffffffff;
+
+  if (ncv_write_reg(priv, NCV_ADDRMASK0L_REGID, regval))
+    {
+      return ERROR;
+    }
+
+  regval = 0x0000ffff;
+
+  if (ncv_write_reg(priv, NCV_ADDRMASK0H_REGID, regval))
+    {
+      return ERROR;
+    }
+
+#endif
+
+  /* enable rx buffer overflow interrupt */
+
+  regval = OA_IMSK0_DEF & ~(1 << OA_IMSK0_RXBOEM_POS);
+
+  if (ncv_write_reg(priv, OA_IMSK0_REGID, regval))
+    {
+      return ERROR;
+    }
+
+  /* setup SPI protocol and set SYNC flag */
+
+  regval =   (1 << OA_CONFIG0_SYNC_POS)
+           | (1 << OA_CONFIG0_CSARFE_POS)
+           | (1 << OA_CONFIG0_ZARFE_POS)
+           | (1 << OA_CONFIG0_RXCTE_POS)  /* a bit lower latency */
+           | (3 << OA_CONFIG0_TXCTHRESH_POS)
+           | (6 << OA_CONFIG0_CPS_POS);
+
+  if (ncv_write_reg(priv, OA_CONFIG0_REGID, regval))
+    {
+      return ERROR;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv_enable
+ *
+ * Description:
+ *   Enable TX and RX on the MAC-PHY.
+ *
+ * Input Parameters:
+ *   priv - pointer to the driver-specific state structure
+ *
+ * Returned Value:
+ *   On success OK is returned, otherwise ERROR is returned.
+ *
+ ****************************************************************************/
+
+static int ncv_enable(FAR struct ncv7410_driver_s *priv)
+{
+  /* enable PHY */
+
+  uint32_t setbits;
+
+  ninfo("Enabling NCV7410\n");
+
+  /* enable RX and TX in PHY */
+
+  setbits = (1 << OA_PHY_CONTROL_LCTL_POS);
+
+  if (ncv_set_clear_bits(priv, OA_PHY_CONTROL_REGID, setbits, 0))
+    {
+      return ERROR;
+    }
+
+  /* enable PHY interrupt */
+
+  setbits = (1 << OA_IMSK0_PHYINTM_POS);
+
+  if (ncv_set_clear_bits(priv, OA_IMSK0_REGID, setbits, 0))
+    {
+      return ERROR;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv_disable
+ *
+ * Description:
+ *   Disable TX and RX on the MAC-PHY.
+ *
+ * Input Parameters:
+ *   priv - pointer to the driver-specific state structure
+ *
+ * Returned Value:
+ *   On success OK is returned, otherwise ERROR is returned.
+ *
+ ****************************************************************************/
+
+static int ncv_disable(FAR struct ncv7410_driver_s *priv)
+{
+  /* disable PHY */
+
+  uint32_t clearbits;
+
+  ninfo("Disabling NCV7410\n");
+
+  /* disable PHY interrupt */
+
+  clearbits = (1 << OA_IMSK0_PHYINTM_POS);
+
+  if (ncv_set_clear_bits(priv, OA_IMSK0_REGID, 0, clearbits))
+    {
+      return ERROR;
+    }
+
+  /* disable RX and TX in PHY */
+
+  clearbits = (1 << OA_PHY_CONTROL_LCTL_POS);
+
+  if (ncv_set_clear_bits(priv, OA_PHY_CONTROL_REGID, 0, clearbits))
+    {
+      return ERROR;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv_init_mac_addr
+ *
+ * Description:
+ *   Read the MAC-PHY's factory-assigned MAC address and copy it into
+ *   the network device state structure.
+ *
+ * Input Parameters:
+ *   priv - pointer to the driver-specific state structure
+ *
+ * Returned Value:
+ *   On success OK is returned, otherwise ERROR is returned.
+ *
+ ****************************************************************************/
+
+static int ncv_init_mac_addr(FAR struct ncv7410_driver_s *priv)
+{
+  uint32_t regval;
+  uint8_t  mac[6];
+
+  if (ncv_read_reg(priv, OA_PHYID_REGID, &regval))
+    {
+      return ERROR;
+    }
+
+  mac[0] = ncv_bitrev8(regval >> 26);
+  mac[1] = ncv_bitrev8(regval >> 18);
+  mac[2] = ncv_bitrev8(regval >> 10);
+
+  if (ncv_read_reg(priv, NCV_MACID1_REGID, &regval))
+    {
+      return ERROR;
+    }
+
+  mac[3] = regval;
+
+  if (ncv_read_reg(priv, NCV_MACID0_REGID, &regval))
+    {
+      return ERROR;
+    }
+
+  mac[4] = regval >> 8;
+  mac[5] = regval;
+
+  memcpy(&priv->dev.netdev.d_mac.ether, &mac, sizeof(struct ether_addr));
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv_reset_driver_buffers
+ *
+ * Description:
+ *   If allocated, release both tx and rx netpackets and reset buffer status
+ *   to the default.
+ *
+ * Input Parameters:
+ *   priv - pointer to the driver-specific state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void ncv_reset_driver_buffers(FAR struct ncv7410_driver_s *priv)
+{
+  priv->txc = 0;
+  priv->rca = 0;
+
+  if (priv->tx_pkt)
+    {
+      ncv_release_tx_packet(priv);
+    }
+
+  if (priv->rx_pkt)
+    {
+      ncv_release_rx_packet(priv);
+    }
+
+  priv->tx_pkt_idx = 0;
+  priv->rx_pkt_idx = 0;
+  priv->tx_pkt_len = 0;
+  priv->rx_pkt_ready = false;
+}
+
+/****************************************************************************
+ * Name: ncv_print_footer
+ *
+ * Description:
+ *   print individual bitfield of a receive chunk footer
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_NET_INFO
+static void ncv_print_footer(uint32_t footer)
+{
+  ninfo("Footer:\n");
+  ninfo("  EXST: %d\n", oa_ext_status(footer));
+  ninfo("  HDRB: %d\n", oa_header_bad(footer));
+  ninfo("  SYNC: %d\n", oa_mac_phy_sync(footer));
+  ninfo("  RCA:  %d\n", oa_rx_available(footer));
+  ninfo("  DV:   %d\n", oa_data_valid(footer));
+  ninfo("  SV:   %d\n", oa_start_valid(footer));
+  ninfo("  SWO:  %d\n", oa_start_word_offset(footer));
+  ninfo("  FD:   %d\n", oa_frame_drop(footer));
+  ninfo("  EV:   %d\n", oa_end_valid(footer));
+  ninfo("  EBO:  %d\n", oa_end_byte_offset(footer));
+  ninfo("  RTSA: %d\n", oa_rx_frame_timestamp_added(footer));
+  ninfo("  RTSP: %d\n", oa_rx_frame_timestamp_parity(footer));
+  ninfo("  TXC:  %d\n", oa_tx_credits(footer));
+}
+#endif
+
+/****************************************************************************
+ * Netdev upperhalf callbacks
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ncv7410_ifup
+ *
+ * Description:
+ *   NuttX callback: Bring up the Ethernet interface
+ *
+ * Input Parameters:
+ *   dev - reference to the NuttX driver state structure
+ *
+ * Returned Values:
+ *   On success OK is returned, otherwise negated errno is returned.
+ *
+ ****************************************************************************/
+
+static int ncv7410_ifup(FAR struct netdev_lowerhalf_s *dev)
+{
+  FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)dev;
+
+  if (priv->ifstate == NCV_INIT_UP)
+    {
+      nerr("Tried to bring NCV7410 interface up when already up\n");
+      return -EINVAL;
+    }
+
+  ninfo("Bringing up NCV7410\n");
+
+  if (priv->ifstate == NCV_RESET)
+    {
+      if (ncv_config(priv) == ERROR)
+        {
+          nerr("Error configuring NCV7410\n");
+          return -EIO;
+        }
+
+      priv->ifstate = NCV_INIT_DOWN;
+    }
+
+  /* set NCV_INIT_UP prior to enabling to allow ncv_interrupt_work right
+   * after MAC-PHY enable
+   */
+
+  priv->ifstate = NCV_INIT_UP;
+
+  if (ncv_enable(priv) == ERROR)
+    {
+      nerr("Error enabling NCV7410\n");
+      priv->ifstate = NCV_INIT_DOWN;
+      return -EIO;
+    }
+
+  /* schedule interrupt work to initialize txc and rca */
+
+  work_queue(NCVWORK, &priv->interrupt_work, ncv_interrupt_work, priv, 0);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv7410_ifdown
+ *
+ * Description:
+ *   NuttX callback: Shut down the Ethernet interface.
+ *
+ * Input Parameters:
+ *   dev - reference to the NuttX driver state structure
+ *
+ * Returned Values:
+ *   On success OK is returned, otherwise negated errno is returned.
+ *
+ ****************************************************************************/
+
+static int ncv7410_ifdown(FAR struct netdev_lowerhalf_s *dev)
+{
+  FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)dev;
+
+  nxmutex_lock(&priv->lock);
+
+  if (priv->ifstate != NCV_INIT_UP)
+    {
+      nxmutex_unlock(&priv->lock);
+      nerr("Tried to bring the NCV7410 interface down but it is not up\n");
+      return -EINVAL;
+    }
+
+  work_cancel(NCVWORK, &priv->interrupt_work);
+  work_cancel(NCVWORK, &priv->io_work);
+
+  if (ncv_disable(priv) == ERROR)
+    {
+      nxmutex_unlock(&priv->lock);
+      nerr("Error disabling NCV7410\n");
+      return -EIO;
+    }
+
+  ncv_reset_driver_buffers(priv);
+
+  priv->ifstate = NCV_INIT_DOWN;
+
+  nxmutex_unlock(&priv->lock);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv7410_transmit
+ *
+ * Description:
+ *   NuttX callback: Transmit the given packet.
+ *
+ * Input Parameters:
+ *   dev - reference to the NuttX driver state structure
+ *   pkt - network packet to be transmitted
+ *
+ * Returned Values:
+ *   On success OK is returned, otherwise negated errno is returned.
+ *
+ ****************************************************************************/
+
+static int ncv7410_transmit(FAR struct netdev_lowerhalf_s *dev,
+                            FAR netpkt_t *pkt)
+{
+  FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)dev;
+
+  nxmutex_lock(&priv->lock);
+
+  if (priv->tx_pkt || priv->ifstate != NCV_INIT_UP)
+    {
+      /* previous tx packet was not yet sent to the network
+       * or the interface was shut down while waiting for the lock
+       */
+
+      nxmutex_unlock(&priv->lock);
+      return -EAGAIN;
+    }
+
+  priv->tx_pkt_idx = 0;
+  priv->tx_pkt_len = netpkt_getdatalen(dev, pkt);
+  priv->tx_pkt = pkt;
+
+  nxmutex_unlock(&priv->lock);
+
+  work_queue(NCVWORK, &priv->io_work, ncv_io_work, priv, 0);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ncv7410_receive
+ *
+ * Description:
+ *   NuttX callback: Claims an rx packet if available.
+ *
+ * Input Parameters:
+ *   dev - reference to the NuttX driver state structure
+ *
+ * Returned Values:
+ *   If the rx packet is ready, its pointer is returned.
+ *   NULL is returned otherwise.
+ *
+ ****************************************************************************/
+
+static FAR netpkt_t *ncv7410_receive(FAR struct netdev_lowerhalf_s *dev)
+{
+  FAR struct ncv7410_driver_s *priv = (FAR struct ncv7410_driver_s *)dev;
+
+  nxmutex_lock(&priv->lock);
+
+  if (priv->rx_pkt_ready)
+    {
+      netpkt_t *retval = priv->rx_pkt;
+      priv->rx_pkt_ready = false;
+      priv->rx_pkt = NULL;
+      nxmutex_unlock(&priv->lock);
+      return retval;
+    }
+
+  nxmutex_unlock(&priv->lock);
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ncv7410_initialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver.
+ *
+ * Input Parameters:
+ *   spi - reference to the SPI driver state data
+ *   irq - irq number of the pin connected to MAC-PHY's interrupt signal
+ *
+ * Returned Value:
+ *   On success OK is returned, otherwise negated errno is returned.
+ *
+ ****************************************************************************/
+
+int ncv7410_initialize(FAR struct spi_dev_s *spi, int irq,
+                       struct ncv7410_config_s *config)
+{
+  FAR struct ncv7410_driver_s   *priv   = NULL;
+  FAR struct netdev_lowerhalf_s *netdev = NULL;
+  int retval;
+
+  /* Allocate the interface structure */
+
+  priv = kmm_zalloc(sizeof(*priv));
+  if (priv == NULL)
+    {
+      nerr("Could not allocate data for ncv7410 priv\n");
+      return -ENOMEM;
+    }
+
+  priv->spi = spi;       /* Save the SPI instance                   */
+  priv->irqnum = irq;    /* Save the Interrupt Request Number       */
+  priv->config = config; /* Save the reference to the configuration */
+
+  /* Reset NCV7410 chip */
+
+  if (ncv_reset(priv))
+    {
+      nerr("Error resetting NCV7410\n");
+      retval = -EIO;
+      goto errout;
+    }
+
+  priv->ifstate = NCV_RESET;
+  ninfo("Resetting NCV7410 OK\n");
+
+  if (ncv_init_mac_addr(priv))
+    {
+      nerr("Error initializing NCV7410 MAC address\n");
+      retval = -EIO;
+      goto errout;
+    }
+
+  ninfo("Initializing MAC address OK\n");
+
+  /* Attach ISR */
+
+  irq_attach(priv->irqnum, ncv_interrupt, priv);
+
+  /* Init lock */
+
+  nxmutex_init(&priv->lock);
+
+  /* Register the device with the OS */
+
+  netdev = &priv->dev;
+  netdev->quota[NETPKT_TX] = NCV7410_TX_QUOTA;
+  netdev->quota[NETPKT_RX] = NCV7410_RX_QUOTA;
+  netdev->ops = &g_ncv7410_ops;
+
+  retval = netdev_lower_register(netdev, NET_LL_ETHERNET);
+  if (retval == OK)
+    {
+      ninfo("Successfully registered NCV7410 network driver\n");
+      return OK;
+    }
+
+  nerr("Error registering NCV7410 network driver: %d\n", retval);
+
+errout:
+  kmm_free(priv);
+  return retval;
+}
diff --git a/drivers/net/ncv7410.h b/drivers/net/ncv7410.h
new file mode 100644
index 0000000000..3638f751f3
--- /dev/null
+++ b/drivers/net/ncv7410.h
@@ -0,0 +1,291 @@
+/****************************************************************************
+ * drivers/net/ncv7410.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 __DRIVERS_NET_NCV7410_H
+#define __DRIVERS_NET_NCV7410_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/bits.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* NuttX SPI mode number for SPI config as defined in OpenAlliance TC6 */
+
+#define OA_SPI_MODE 0
+
+/* Number of bits in a SPI word */
+
+#define OA_SPI_NBITS 8
+
+#define NCV_CHUNK_DEFAULT_PAYLOAD_SIZE 64
+#define NCV_CHUNK_DEFAULT_SIZE (NCV_CHUNK_DEFAULT_PAYLOAD_SIZE + 4)
+
+typedef uint32_t oa_regid_t;
+
+#define OA_MAKE_REGID(mms, addr) \
+    (((uint32_t)(mms) << 16) | ((uint32_t)(addr) & 0xFFFF))
+
+#define OA_REGID_GET_MMS(regid) ((uint8_t)((regid >> 16) & 0xF))
+#define OA_REGID_GET_ADDR(regid) ((uint16_t)(regid & 0xFFFF))
+
+#define OA_IDVER_MMS              0
+#define OA_IDVER_ADDR             0x0U
+#define OA_IDVER_REGID            OA_MAKE_REGID(OA_IDVER_MMS, OA_IDVER_ADDR)
+
+#define OA_PHYID_MMS              0
+#define OA_PHYID_ADDR             0x1U
+#define OA_PHYID_REGID            OA_MAKE_REGID(OA_PHYID_MMS, OA_PHYID_ADDR)
+#define OA_PHYID_OUI_MASK         GENMASK(31, 10)
+#define OA_PHYID_OUI_POS          10
+#define OA_PHYID_MODEL_MASK       GENMASK(9, 4)
+#define OA_PHYID_MODEL_POS        4
+#define OA_PHYID_REV_MASK         GENMASK(3, 0)
+#define OA_PHYID_REV_POS          0
+
+#define OA_STDCAP_MMS             0
+#define OA_STDCAP_ADDR            0x2U
+#define OA_STDCAP_REGID           OA_MAKE_REGID(OA_STDCAP_MMS, OA_STDCAP_ADDR)
+
+#define OA_RESET_MMS              0
+#define OA_RESET_ADDR             0x3U
+#define OA_RESET_REGID            OA_MAKE_REGID(OA_RESET_MMS, OA_RESET_ADDR)
+#define OA_RESET_SWRESET_MASK     BIT(0)
+#define OA_RESET_SWRESET_POS      0
+
+#define OA_CONFIG0_MMS            0
+#define OA_CONFIG0_ADDR           0x4U
+#define OA_CONFIG0_REGID          OA_MAKE_REGID(OA_CONFIG0_MMS, 
OA_CONFIG0_ADDR)
+#define OA_CONFIG0_SYNC_MASK      BIT(15)
+#define OA_CONFIG0_SYNC_POS       15
+#define OA_CONFIG0_TXFCSVE_MASK   BIT(14)
+#define OA_CONFIG0_TXFCSVE_POS    14
+#define OA_CONFIG0_CSARFE_MASK    BIT(13)
+#define OA_CONFIG0_CSARFE_POS     13
+#define OA_CONFIG0_ZARFE_MASK     BIT(12)
+#define OA_CONFIG0_ZARFE_POS      12
+#define OA_CONFIG0_TXCTHRESH_MASK GENMASK(11, 10)
+#define OA_CONFIG0_TXCTHRESH_POS  10
+#define OA_CONFIG0_TXCTE_MASK     BIT(9)
+#define OA_CONFIG0_TXCTE_POS      9
+#define OA_CONFIG0_RXCTE_MASK     BIT(8)
+#define OA_CONFIG0_RXCTE_POS      8
+#define OA_CONFIG0_FTSE_MASK      BIT(7)
+#define OA_CONFIG0_FTSE_POS       7
+#define OA_CONFIG0_FTSS_MASK      BIT(6)
+#define OA_CONFIG0_FTSS_POS       6
+#define OA_CONFIG0_PROTE_MASK     BIT(5)
+#define OA_CONFIG0_PROTE_POS      5
+#define OA_CONFIG0_SEQE_MASK      BIT(4)
+#define OA_CONFIG0_SEQE_POS       4
+#define OA_CONFIG0_CPS_MASK       GENMASK(2, 0)
+#define OA_CONFIG0_CPS_POS        0
+
+#define OA_STATUS0_MMS            0
+#define OA_STATUS0_ADDR           0x8U
+#define OA_STATUS0_REGID          OA_MAKE_REGID(OA_STATUS0_MMS, 
OA_STATUS0_ADDR)
+#define OA_STATUS0_RESETC_MASK    BIT(6)
+#define OA_STATUS0_RESETC_POS     6
+#define OA_STATUS0_HDRE_MASK      BIT(5)
+#define OA_STATUS0_HDRE_POS       5
+
+#define OA_BUFSTS_MMS             0
+#define OA_BUFSTS_ADDR            0xBU
+#define OA_BUFSTS_REGID           OA_MAKE_REGID(OA_BUFSTS_MMS, OA_BUFSTS_ADDR)
+
+#define OA_IMSK0_MMS              0
+#define OA_IMSK0_ADDR             0xCU
+#define OA_IMSK0_REGID            OA_MAKE_REGID(OA_IMSK0_MMS, OA_IMSK0_ADDR)
+#define OA_IMSK0_DEF              0x1FBFU
+#define OA_IMSK0_PHYINTM_MASK     BIT(7)
+#define OA_IMSK0_PHYINTM_POS      7
+#define OA_IMSK0_RXBOEM_MASK      BIT(3)
+#define OA_IMSK0_RXBOEM_POS       3
+
+#define OA_PHY_CONTROL_MMS        0
+#define OA_PHY_CONTROL_ADDR       0xFF00U
+#define OA_PHY_CONTROL_REGID      OA_MAKE_REGID(OA_PHY_CONTROL_MMS, 
OA_PHY_CONTROL_ADDR)
+#define OA_PHY_CONTROL_LCTL_POS   12
+
+#define OA_PHY_STATUS_MMS         0
+#define OA_PHY_STATUS_ADDR        0xFF01U
+#define OA_PHY_STATUS_REGID       OA_MAKE_REGID(OA_PHY_STATUS_MMS, 
OA_PHY_STATUS_ADDR)
+
+/* registers specific to ncv7410 */
+
+#define NCV_MAC_CONTROL0_MMS      1
+#define NCV_MAC_CONTROL0_ADDR     0x0U
+#define NCV_MAC_CONTROL0_REGID    OA_MAKE_REGID(NCV_MAC_CONTROL0_MMS, 
NCV_MAC_CONTROL0_ADDR)
+#define NCV_MAC_CONTROL0_ADRF_POS 16
+#define NCV_MAC_CONTROL0_FCSA_POS 8
+#define NCV_MAC_CONTROL0_TXEN_POS 1
+#define NCV_MAC_CONTROL0_RXEN_POS 0
+
+#define NCV_ADDRFILT0L_MMS        1
+#define NCV_ADDRFILT0L_ADDR       0x10U
+#define NCV_ADDRFILT0L_REGID      OA_MAKE_REGID(NCV_ADDRFILT0L_MMS, 
NCV_ADDRFILT0L_ADDR)
+
+#define NCV_ADDRFILT0H_MMS        1
+#define NCV_ADDRFILT0H_ADDR       0x11U
+#define NCV_ADDRFILT0H_REGID      OA_MAKE_REGID(NCV_ADDRFILT0H_MMS, 
NCV_ADDRFILT0H_ADDR)
+
+#define NCV_ADDRMASK0L_MMS        1
+#define NCV_ADDRMASK0L_ADDR       0x20U
+#define NCV_ADDRMASK0L_REGID      OA_MAKE_REGID(NCV_ADDRMASK0L_MMS, 
NCV_ADDRMASK0L_ADDR)
+
+#define NCV_ADDRMASK0H_MMS        1
+#define NCV_ADDRMASK0H_ADDR       0x21U
+#define NCV_ADDRMASK0H_REGID      OA_MAKE_REGID(NCV_ADDRMASK0H_MMS, 
NCV_ADDRMASK0H_ADDR)
+
+#define NCV_DIO_CONFIG_MMS        12
+#define NCV_DIO_CONFIG_ADDR       0x0012U
+#define NCV_DIO_CONFIG_REGID      OA_MAKE_REGID(NCV_DIO_CONFIG_MMS, 
NCV_DIO_CONFIG_ADDR)
+#define NCV_DIO_CONFIG_DEF        0x6060
+#define NCV_DIO0_FUNC_POS         1
+#define NCV_DIO1_FUNC_POS         9
+#define NCV_DIO0_OUT_VAL_POS      0
+#define NCV_DIO1_OUT_VAL_POS      8
+#define NCV_DIO_TRISTATE_FUNC     0x0
+#define NCV_DIO_GPIO_FUNC         0x1
+#define NCV_DIO_SFD_TX_FUNC       0x2
+#define NCV_DIO_SFD_RX_FUNC       0x3
+#define NCV_DIO_LINK_CTRL_FUNC    0x4
+#define NCV_DIO_SFD_TXRX_FUNC     0xB
+#define NCV_DIO_TXRX_FUNC         0xF
+
+#define NCV_MACID0_MMS            12
+#define NCV_MACID0_ADDR           0x1002U
+#define NCV_MACID0_REGID          OA_MAKE_REGID(NCV_MACID0_MMS, 
NCV_MACID0_ADDR)
+#define NCV_MACID0_MASK           GENMASK(15, 0)
+#define NCV_MACID0_POS            0
+
+#define NCV_MACID1_MMS            12
+#define NCV_MACID1_ADDR           0x1003U
+#define NCV_MACID1_REGID          OA_MAKE_REGID(NCV_MACID1_MMS, 
NCV_MACID1_ADDR)
+#define NCV_MACID1_MASK           GENMASK(7, 0)
+#define NCV_MACID1_POS            0
+
+/* OA Data Transaction and Control Transaction protocols bitfields */
+
+/* Common bitfields */
+
+#define OA_DNC_MASK  BIT(31)
+#define OA_DNC_POS   31
+
+#define OA_HDRB_MASK BIT(30)
+#define OA_HDRB_POS  30
+
+#define OA_VS_MASK   GENMASK(23, 22)
+#define OA_VS_POS    22
+
+#define OA_DV_MASK   BIT(21)
+#define OA_DV_POS    21
+
+#define OA_SV_MASK   BIT(20)
+#define OA_SV_POS    20
+
+#define OA_SWO_MASK  GENMASK(19, 16)
+#define OA_SWO_POS   16
+
+#define OA_EV_MASK   BIT(14)
+#define OA_EV_POS    14
+
+#define OA_EBO_MASK  GENMASK(13, 8)
+#define OA_EBO_POS   8
+
+#define OA_P_MASK    BIT(0)
+#define OA_P_POS     0
+
+/* Control Transaction Protocol header bitfields */
+
+#define OA_WNR_MASK  BIT(29)
+#define OA_WNR_POS   29
+
+#define OA_AID_MASK  BIT(28)
+#define OA_AID_POS   28
+
+#define OA_MMS_MASK  GENMASK(27, 24)
+#define OA_MMS_POS   24
+
+#define OA_ADDR_MASK GENMASK(23, 8)
+#define OA_ADDR_POS  8
+
+#define OA_LEN_MASK  GENMASK(7, 1)
+#define OA_LEN_POS   1
+
+/* Transmit data header bitfields */
+
+#define OA_SEQ_MASK  BIT(30)
+#define OA_SEQ_POS   30
+
+#define OA_NORX_MASK BIT(29)
+#define OA_NORX_POS  29
+
+#define OA_TSC_MASK  GENMASK(7, 6)
+#define OA_TSC_POS   6
+
+/* Receive data footer bitfields */
+
+#define OA_EXST_MASK BIT(31)
+#define OA_EXST_POS  31
+
+#define OA_SYNC_MASK BIT(29)
+#define OA_SYNC_POS  29
+
+#define OA_RCA_MASK  GENMASK(28, 24)
+#define OA_RCA_POS   24
+
+#define OA_FD_MASK   BIT(15)
+#define OA_FD_POS    15
+
+#define OA_RTSA_MASK BIT(7)
+#define OA_RTSA_POS  7
+
+#define OA_RTSP_MASK BIT(6)
+#define OA_RTSP_POS  6
+
+#define OA_TXC_MASK  GENMASK(5, 1)
+#define OA_TXC_POS   1
+
+#define _oa_control_field(f, fieldname) \
+    ((int) ((f & OA_##fieldname##_MASK) >> OA_##fieldname##_POS))
+
+#define oa_tx_credits(f)                _oa_control_field(f, TXC)
+#define oa_rx_available(f)              _oa_control_field(f, RCA)
+#define oa_header_bad(f)                _oa_control_field(f, HDRB)
+#define oa_ext_status(f)                _oa_control_field(f, EXST)
+#define oa_data_valid(f)                _oa_control_field(f, DV)
+#define oa_start_valid(f)               _oa_control_field(f, SV)
+#define oa_start_word_offset(f)         _oa_control_field(f, SWO)
+#define oa_end_valid(f)                 _oa_control_field(f, EV)
+#define oa_end_byte_offset(f)           _oa_control_field(f, EBO)
+#define oa_frame_drop(f)                _oa_control_field(f, FD)
+#define oa_rx_frame_timestamp_added(f)  _oa_control_field(f, RTSA)
+#define oa_rx_frame_timestamp_parity(f) _oa_control_field(f, RTSP)
+#define oa_mac_phy_sync(f)              _oa_control_field(f, SYNC)
+
+#endif /* __DRIVERS_NET_NCV7410_H */
diff --git a/include/nuttx/net/ncv7410.h b/include/nuttx/net/ncv7410.h
new file mode 100644
index 0000000000..92babc0c7d
--- /dev/null
+++ b/include/nuttx/net/ncv7410.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+ * include/nuttx/net/ncv7410.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 __INCLUDE_NUTTX_NET_NCV7410_H
+#define __INCLUDE_NUTTX_NET_NCV7410_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* A reference to a structure of this type must be passed to the NCV7410
+ * driver when the driver is instantiated. This structure provides
+ * information about the configuration of the NCV7410.
+ *
+ * Memory for this structure is provided by the caller. It is not copied by
+ * the driver and is presumed to persist while the driver is active.
+ */
+
+struct ncv7410_config_s
+{
+  uint32_t id;
+
+  /* TODO: include hooks for interrupt logic */
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ncv7410_initialize
+ *
+ * Description:
+ *   Initialize the Ethernet driver.
+ *
+ * Input Parameters:
+ *   spi - reference to the SPI driver state data
+ *   irq - irq number of the pin connected to MAC-PHY's interrupt signal
+ *
+ * Returned Value:
+ *   On success OK is returned, otherwise negated errno is returned.
+ *
+ ****************************************************************************/
+
+struct spi_dev_s; /* forward declaration, see nuttx/spi/spi.h */
+int ncv7410_initialize(FAR struct spi_dev_s *spi, int irq,
+                       struct ncv7410_config_s *config);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_NUTTX_NET_NCV7410_H */

Reply via email to