summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-08-25 11:21:54 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-08-25 11:21:54 -0600
commit1bc3365333b0243840cb87a9f8a267d77efdc651 (patch)
tree5d125bd2027ac04ef2e4128f65f674b6a4840a3b
parent5a080e4c38ba1fd5c9d395ef7186716056f4dd55 (diff)
downloadnuttx-1bc3365333b0243840cb87a9f8a267d77efdc651.tar.gz
nuttx-1bc3365333b0243840cb87a9f8a267d77efdc651.tar.bz2
nuttx-1bc3365333b0243840cb87a9f8a267d77efdc651.zip
Add ENCX24J600 Ethernet driver and support for the ENCX24J600 with the Olimex STM32 P107 board. From Max Holtberg
-rw-r--r--nuttx/ChangeLog7
-rw-r--r--nuttx/configs/olimex-stm32-p107/src/Makefile8
-rw-r--r--nuttx/configs/olimex-stm32-p107/src/p107-internal.h88
-rw-r--r--nuttx/configs/olimex-stm32-p107/src/up_boot.c14
-rw-r--r--nuttx/configs/olimex-stm32-p107/src/up_encx24j600.c196
-rw-r--r--nuttx/configs/olimex-stm32-p107/src/up_spi.c154
-rw-r--r--nuttx/drivers/net/Kconfig60
-rw-r--r--nuttx/drivers/net/Make.defs4
-rw-r--r--nuttx/drivers/net/encx24j600.c2382
-rw-r--r--nuttx/drivers/net/encx24j600.h423
-rw-r--r--nuttx/include/nuttx/net/encx24j600.h181
11 files changed, 3509 insertions, 8 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index 7a82c230d..775406486 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -5445,5 +5445,8 @@
From Max Holtzberg (2013-8-25).
* arch/arm/src/sama5/sam_ohci.c: SAMA5 OHCI is again functional by
itself after all of the changes to integrate with EHCI. (2013-8-25).
-
-
+ * drivers/net/encx24j600.c/.h and include/nuttx/net/encx24j600.h:
+ Support the Microchip ENCX24J600 Ethernet driver from Max Holtberg
+ (2013-8-25).
+ * configs/olimex-stm32-p107: Incorporate ENCX24J600 support for the
+ Olimex STM32 P107 board. From Max Holtzberg (2013-8-25).
diff --git a/nuttx/configs/olimex-stm32-p107/src/Makefile b/nuttx/configs/olimex-stm32-p107/src/Makefile
index 66bd59530..83211584d 100644
--- a/nuttx/configs/olimex-stm32-p107/src/Makefile
+++ b/nuttx/configs/olimex-stm32-p107/src/Makefile
@@ -37,15 +37,19 @@
CFLAGS += -I$(TOPDIR)/sched
-ASRCS =
+ASRCS =
AOBJS = $(ASRCS:.S=$(OBJEXT))
-CSRCS = up_boot.c
+CSRCS = up_boot.c up_spi.c
ifeq ($(CONFIG_CAN),y)
CSRCS += up_can.c
endif
+ifeq ($(CONFIG_ENCX24J600),y)
+CSRCS += up_encx24j600.c
+endif
+
COBJS = $(CSRCS:.c=$(OBJEXT))
SRCS = $(ASRCS) $(CSRCS)
diff --git a/nuttx/configs/olimex-stm32-p107/src/p107-internal.h b/nuttx/configs/olimex-stm32-p107/src/p107-internal.h
new file mode 100644
index 000000000..f699e70d3
--- /dev/null
+++ b/nuttx/configs/olimex-stm32-p107/src/p107-internal.h
@@ -0,0 +1,88 @@
+/******************************************************************************
+ * configs/olimex-stm32-p107/src/p107-internal.h
+ *
+ * Copyright (C) 2013 Max Holtzberg. All rights reserved.
+ * Author: Max Holtzberg <mholtzberg@uvc-ingenieure.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************/
+#ifndef __CONFIGS_OLIMEX_STM32_P107_SRC_INTERNAL_H
+#define __CONFIGS_OLIMEX_STM32_P107_SRC_INTERNAL_H
+
+/******************************************************************************
+ * Included Files
+ ******************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+#include <stdint.h>
+
+/******************************************************************************
+ * Definitions
+ ******************************************************************************/
+
+/* ENCX24J600
+ *
+ * --- ------ -------------- ---------------------------------------------------
+ * PIN NAME SIGNAL NOTES
+ * --- ------ -------------- ---------------------------------------------------
+ *
+ * 54 PB15 PB15-CS_UEXT ENCX24J600 #CS
+ * 78 PC10 PC10-SPI3-SCK ENCX24J600 SCK
+ * 79 PC11 PC11-SPI3-MISO ENCX24J600 MISO
+ * 80 PC12 PC12-SPI3-MOSI ENCX24J600 MOSI
+ * 95 PB8 PB8 ENCX24J600 #Interrupt
+ */
+
+
+#ifdef CONFIG_ENCX24J600
+# define GPIO_ENCX24J600_CS (GPIO_OUTPUT|GPIO_CNF_OUTPP|GPIO_MODE_50MHz| \
+ GPIO_OUTPUT_SET|GPIO_PORTB|GPIO_PIN15)
+# define GPIO_ENCX24J600_INTR (GPIO_INPUT|GPIO_CNF_INFLOAT|GPIO_MODE_INPUT| \
+ GPIO_EXTI|GPIO_PORTB|GPIO_PIN8)
+#endif
+
+#ifndef __ASSEMBLY__
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: stm32_spiinitialize
+ *
+ * Description:
+ * Called to configure SPI chip select GPIO pins for the M3 Wildfire board.
+ *
+ ************************************************************************************/
+
+void weak_function stm32_spiinitialize(void);
+
+#endif /* __ASSEMBLY__ */
+#endif /* __CONFIGS_OLIMEX_STM32_P107_SRC_INTERNAL_H */
diff --git a/nuttx/configs/olimex-stm32-p107/src/up_boot.c b/nuttx/configs/olimex-stm32-p107/src/up_boot.c
index d7ece5a1e..d10b143ec 100644
--- a/nuttx/configs/olimex-stm32-p107/src/up_boot.c
+++ b/nuttx/configs/olimex-stm32-p107/src/up_boot.c
@@ -39,12 +39,11 @@
************************************************************************************/
#include <nuttx/config.h>
-
#include <debug.h>
-
#include <arch/board/board.h>
#include "up_arch.h"
+#include "p107-internal.h"
/************************************************************************************
* Pre-processor Definitions
@@ -70,4 +69,15 @@
void stm32_boardinitialize(void)
{
+ /* Configure SPI chip selects if 1) SPI is not disabled, and 2) the weak function
+ * stm32_spiinitialize() has been brought into the link.
+ */
+
+#if defined(CONFIG_STM32_SPI3)
+ if (stm32_spiinitialize)
+ {
+ stm32_spiinitialize();
+ }
+#endif
+
}
diff --git a/nuttx/configs/olimex-stm32-p107/src/up_encx24j600.c b/nuttx/configs/olimex-stm32-p107/src/up_encx24j600.c
new file mode 100644
index 000000000..d8af313e3
--- /dev/null
+++ b/nuttx/configs/olimex-stm32-p107/src/up_encx24j600.c
@@ -0,0 +1,196 @@
+/****************************************************************************
+ * configs/olimex-stm32-p107/src/up_encx24j600.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/spi/spi.h>
+#include <nuttx/net/encx24j600.h>
+
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "up_arch.h"
+#include "up_internal.h"
+#include "p107-internal.h"
+
+#ifdef CONFIG_ENCX24J600
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+
+/* ENCX24J600
+ *
+ * --- ------ -------------- ------------------------------------------------
+ * PIN NAME SIGNAL NOTES
+ * --- ------ -------------- ------------------------------------------------
+ *
+ * 54 PB15 PB15-CS_UEXT ENCX24J600 #CS
+ * 78 PC10 PC10-SPI3-SCK ENCX24J600 SCK
+ * 79 PC11 PC11-SPI3-MISO ENCX24J600 MISO
+ * 80 PC12 PC12-SPI3-MOSI ENCX24J600 MOSI
+ * 95 PB8 PB8 ENCX24J600 #Interrupt
+ */
+
+/* ENCX24J600 is on SPI3 */
+
+#ifndef CONFIG_STM32_SPI3
+# error "Need CONFIG_STM32_SPI3 in the configuration"
+#endif
+
+/* SPI Assumptions **********************************************************/
+
+#define ENCX24J600_SPI_PORTNO 3 /* On SPI1 */
+#define ENCX24J600_DEVNO 0 /* Only one ENCX24J600 */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct stm32_lower_s
+{
+ const struct enc_lower_s lower; /* Low-level MCU interface */
+ xcpt_t handler; /* ENCX24J600 interrupt handler */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int up_attach(FAR const struct enc_lower_s *lower, xcpt_t handler);
+static void up_enable(FAR const struct enc_lower_s *lower);
+static void up_disable(FAR const struct enc_lower_s *lower);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* The ENCX24J600 normal provides interrupts to the MCU via a GPIO pin. The
+ * following structure provides an MCU-independent mechanixm for controlling
+ * the ENCX24J600 GPIO interrupt.
+ */
+
+static struct stm32_lower_s g_enclower =
+{
+ .lower =
+ {
+ .attach = up_attach,
+ .enable = up_enable,
+ .disable = up_disable
+ },
+ .handler = NULL,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: struct enc_lower_s methods
+ ****************************************************************************/
+
+static int up_attach(FAR const struct enc_lower_s *lower, xcpt_t handler)
+{
+ FAR struct stm32_lower_s *priv = (FAR struct stm32_lower_s *)lower;
+
+ /* Just save the handler for use when the interrupt is enabled */
+
+ priv->handler = handler;
+ return OK;
+}
+
+static void up_enable(FAR const struct enc_lower_s *lower)
+{
+ FAR struct stm32_lower_s *priv = (FAR struct stm32_lower_s *)lower;
+
+ DEBUGASSERT(priv->handler);
+ (void)stm32_gpiosetevent(GPIO_ENCX24J600_INTR, false, true, true, priv->handler);
+}
+
+static void up_disable(FAR const struct enc_lower_s *lower)
+{
+ (void)stm32_gpiosetevent(GPIO_ENCX24J600_INTR, false, true, true, NULL);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_netinitialize
+ ****************************************************************************/
+
+void up_netinitialize(void)
+{
+ FAR struct spi_dev_s *spi;
+ int ret;
+
+ /* Assumptions:
+ * 1) ENCX24J600 pins were configured in up_spi.c early in the boot-up phase.
+ * 2) Clocking for the SPI1 peripheral was also provided earlier in boot-up.
+ */
+
+ spi = up_spiinitialize(ENCX24J600_SPI_PORTNO);
+ if (!spi)
+ {
+ nlldbg("Failed to initialize SPI port %d\n", ENCX24J600_SPI_PORTNO);
+ return;
+ }
+
+ /* Bind the SPI port to the ENCX24J600 driver */
+
+ ret = enc_initialize(spi, &g_enclower.lower, ENCX24J600_DEVNO);
+
+ if (ret < 0)
+ {
+ nlldbg("Failed to bind SPI port %d ENCX24J600 device %d: %d\n",
+ ENCX24J600_SPI_PORTNO, ENCX24J600_DEVNO, ret);
+ return;
+ }
+
+ nllvdbg("Bound SPI port %d to ENCX24J600 device %d\n",
+ ENCX24J600_SPI_PORTNO, ENCX24J600_DEVNO);
+}
+
+#endif /* CONFIG_ENCX24J600 */
diff --git a/nuttx/configs/olimex-stm32-p107/src/up_spi.c b/nuttx/configs/olimex-stm32-p107/src/up_spi.c
new file mode 100644
index 000000000..c0e597d22
--- /dev/null
+++ b/nuttx/configs/olimex-stm32-p107/src/up_spi.c
@@ -0,0 +1,154 @@
+/************************************************************************************
+ * configs/olimex-stm32-p107/src/up_spi.c
+ *
+ * Copyright (C) 2013 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ************************************************************************************/
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <debug.h>
+
+#include <nuttx/spi/spi.h>
+#include <arch/board/board.h>
+
+#include "up_arch.h"
+#include "chip.h"
+#include "stm32.h"
+#include "p107-internal.h"
+
+#if defined(CONFIG_STM32_SPI3)
+
+/************************************************************************************
+ * Definitions
+ ************************************************************************************/
+
+/* Enables debug output from this file (needs CONFIG_DEBUG too) */
+
+#undef SPI_DEBUG /* Define to enable debug */
+#undef SPI_VERBOSE /* Define to enable verbose debug */
+
+#ifdef SPI_DEBUG
+# define spidbg lldbg
+# ifdef SPI_VERBOSE
+# define spivdbg lldbg
+# else
+# define spivdbg(x...)
+# endif
+#else
+# undef SPI_VERBOSE
+# define spidbg(x...)
+# define spivdbg(x...)
+#endif
+
+/************************************************************************************
+ * Private Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: stm32_spiinitialize
+ *
+ * Description:
+ * Called to configure SPI chip select GPIO pins for the Olimex stm32-p107 board.
+ *
+ ************************************************************************************/
+
+void weak_function stm32_spiinitialize(void)
+{
+ /* NOTE: Clocking for SPI3 was already provided in stm32_rcc.c.
+ * Configurations of SPI pins is performed in stm32_spi.c.
+ * Here, we only initialize chip select pins unique to the board
+ * architecture.
+ */
+
+ /* Configure ENCX24J600 SPI1 CS (also RESET and interrupt pins) */
+
+#if defined(CONFIG_ENCX24J600) && defined(CONFIG_STM32_SPI3)
+ stm32_configgpio(GPIO_ENCX24J600_CS);
+ stm32_configgpio(GPIO_ENCX24J600_INTR);
+#endif
+}
+
+/****************************************************************************
+ * Name: stm32_spi1/2/3select and stm32_spi1/2/3status
+ *
+ * Description:
+ * The external functions, stm32_spi1/2/3select and stm32_spi1/2/3status must be
+ * provided by board-specific logic. They are implementations of the select
+ * and status methods of the SPI interface defined by struct spi_ops_s (see
+ * include/nuttx/spi/spi.h). All other methods (including up_spiinitialize())
+ * are provided by common STM32 logic. To use this common SPI logic on your
+ * board:
+ *
+ * 1. Provide logic in stm32_boardinitialize() to configure SPI chip select
+ * pins.
+ * 2. Provide stm32_spi1/2/3select() and stm32_spi1/2/3status() functions in your
+ * board-specific logic. These functions will perform chip selection and
+ * status operations using GPIOs in the way your board is configured.
+ * 3. Add a calls to up_spiinitialize() in your low level application
+ * initialization logic
+ * 4. The handle returned by up_spiinitialize() may then be used to bind the
+ * SPI driver to higher level logic (e.g., calling
+ * mmcsd_spislotinitialize(), for example, will bind the SPI driver to
+ * the SPI MMC/SD driver).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_SPI3
+void stm32_spi3select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected)
+{
+ spidbg("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert");
+
+ if (devid == SPIDEV_ETHERNET)
+ {
+ /* Set the GPIO low to select and high to de-select */
+
+ stm32_gpiowrite(GPIO_ENCX24J600_CS, !selected);
+ }
+}
+
+uint8_t stm32_spi3status(FAR struct spi_dev_s *dev, enum spi_dev_e devid)
+{
+ return SPI_STATUS_PRESENT;
+}
+#endif
+
+#endif /* CONFIG_STM32_SPI3 */
diff --git a/nuttx/drivers/net/Kconfig b/nuttx/drivers/net/Kconfig
index d8878ecaf..346a51e60 100644
--- a/nuttx/drivers/net/Kconfig
+++ b/nuttx/drivers/net/Kconfig
@@ -16,7 +16,7 @@ config NET_CS89x0
depends on EXPERIMENTAL
---help---
Under construction -- do not use
-
+
config ENC28J60
bool "Microchip ENC28J60 support"
default n
@@ -79,6 +79,62 @@ config ENC28J60_REGDEBUG
endif
+config ENCX24J600
+ bool "Microchip ENCX24J600 support"
+ default n
+ select SPI
+ ---help---
+ References:
+ ENC424J600/624J600 Data Sheet Stand-Alone 10/100 Ethernet Controller
+ with SPI or Parallel Interface DS39935B, 2009 Microchip Technology Inc.
+
+if ENCX24J600
+config ENC28J60_NINTERFACES
+ int "Number of physical ENCX24J600"
+ default 1
+ range 1,1
+ ---help---
+ Specifies the number of physical ENCX24J600
+ devices that will be supported.
+
+config ENCX24J600_SPIMODE
+ int "SPI mode"
+ default 0
+ ---help---
+ Controls the SPI mode. The ENCX24J600 spec says that it supports SPI
+ mode 0,0 only: "The implementation used on this device supports SPI
+ mode 0,0 only. In addition, the SPI port requires that SCK be at Idle
+ in a low state; selectable clock polarity is not supported."
+ However, sometimes you need to tinker with these things.
+
+config ENCX24J600_FREQUENCY
+ int "SPI frequency"
+ default 14000000
+ ---help---
+ Define to use a different bus frequency
+
+config ENCX24J600_STATS
+ bool "Network statistics support"
+ default n
+ ---help---
+ Collect network statistics
+
+config ENCX24J600_DUMPPACKET
+ bool "Dump Packets"
+ default n
+ ---help---
+ If selected, the ENCX24J600 driver will dump the contents of each
+ packet to the console.
+
+config ENCX24J600_REGDEBUG
+ bool "Register-Level Debug"
+ default n
+ depends on DEBUG && DEBUG_NET
+ ---help---
+ Enable very low-level register access debug. Depends on DEBUG and DEBUG_NET.
+
+endif
+
config NET_E1000
bool "E1000 support"
default n
@@ -86,7 +142,7 @@ config NET_E1000
config NET_SLIP
bool "SLIP (serial line) support"
default n
- ---help---
+ ---help---
Reference: RFC 1055
config NET_VNET
diff --git a/nuttx/drivers/net/Make.defs b/nuttx/drivers/net/Make.defs
index bb0d6a718..3d34aa971 100644
--- a/nuttx/drivers/net/Make.defs
+++ b/nuttx/drivers/net/Make.defs
@@ -51,6 +51,10 @@ ifeq ($(CONFIG_ENC28J60),y)
CSRCS += enc28j60.c
endif
+ifeq ($(CONFIG_ENCX24J600),y)
+ CSRCS += encx24j600.c
+endif
+
ifeq ($(CONFIG_NET_VNET),y)
CSRCS += vnet.c
endif
diff --git a/nuttx/drivers/net/encx24j600.c b/nuttx/drivers/net/encx24j600.c
new file mode 100644
index 000000000..6efef1a7a
--- /dev/null
+++ b/nuttx/drivers/net/encx24j600.c
@@ -0,0 +1,2382 @@
+/****************************************************************************
+ * drivers/net/encx24j600.c
+ *
+ * Copyright (C) 2013 UVC Ingenieure. All rights reserved.
+ * Author: Max Holtberg <mh@uvc.de>
+ *
+ * References:
+ * - ENC424J600/624J600 Data Sheet, Stand-Alone 10/100 Ethernet Controller
+ * with SPI or Parallel Interface, DS39935C, 2010 Microchip Technology Inc.
+ *
+ * Derived from enc28j60 driver written by:
+ *
+ * Copyright (C) 2010-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#if defined(CONFIG_NET) && defined(CONFIG_ENCX24J600)
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <wdog.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/clock.h>
+#include <nuttx/net/encx24j600.h>
+
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+#include "encx24j600.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* ENCX24J600 Configuration Settings:
+ *
+ * CONFIG_ENCX24J600 - Enabled ENCX24J600 support
+ * CONFIG_ENCX24J600_SPIMODE - Controls the SPI mode
+ * CONFIG_ENCX24J600_FREQUENCY - Define to use a different bus frequency
+ * CONFIG_ENCX24J600_NINTERFACES - Specifies the number of physical ENCX24J600
+ * devices that will be supported.
+ * CONFIG_ENCX24J600_STATS - Collect network statistics
+ */
+
+/* The ENCX24J600 spec says that it supports SPI mode 0,0 only: "The
+ * implementation used on this device supports SPI mode 0,0 only. In
+ * addition, the SPI port requires that SCK be at Idle in a low state;
+ * selectable clock polarity is not supported." However, sometimes you
+ * need to tinker with these things.
+ */
+
+#ifndef CONFIG_ENCX24J600_SPIMODE
+# define CONFIG_ENCX24J600_SPIMODE SPIDEV_MODE0
+#endif
+
+/* CONFIG_ENCX24J600_NINTERFACES determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#ifndef CONFIG_ENCX24J600_NINTERFACES
+# define CONFIG_ENCX24J600_NINTERFACES 1
+#endif
+
+/* CONFIG_NET_BUFSIZE must always be defined */
+
+#if !defined(CONFIG_NET_BUFSIZE) && (CONFIG_NET_BUFSIZE <= MAX_FRAMELEN)
+# error "CONFIG_NET_BUFSIZE is not valid for the ENCX24J600"
+#endif
+
+/* We need to have the work queue to handle SPI interrupts */
+
+#ifndef CONFIG_SCHED_WORKQUEUE
+# error "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
+#endif
+
+/* CONFIG_ENCX24J600_DUMPPACKET will dump the contents of each packet to the console. */
+
+#ifdef CONFIG_ENCX24J600_DUMPPACKET
+# define enc_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+# define enc_dumppacket(m,a,n)
+#endif
+
+/* The ENCX24J600 will not do interrupt level processing */
+
+#ifndef CONFIG_NET_NOINTS
+# warning "CONFIG_NET_NOINTS should be set"
+#endif
+
+/* Low-level register debug */
+
+#if !defined(CONFIG_DEBUG) || !defined(CONFIG_DEBUG_NET)
+# undef CONFIG_ENCX24J600_REGDEBUG
+#endif
+
+/* Timing *******************************************************************/
+
+/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */
+
+#define ENC_WDDELAY (1*CLK_TCK)
+#define ENC_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define ENC_TXTIMEOUT (60*CLK_TCK)
+
+/* Poll timeout */
+
+#define ENC_POLLTIMEOUT MSEC2TICK(50)
+
+/* Packet Memory ************************************************************/
+
+/* Packet memory layout */
+
+#define ALIGNED_BUFSIZE ((CONFIG_NET_BUFSIZE + 1) & ~1) /* Address has to be even */
+#define PKTMEM_TX_START PKTMEM_START /* Start TX buffer at teh beginning of SRAM */
+#define PKTMEM_TX_ENDP1 ALIGNED_BUFSIZE /* Allow TX buffer for one frame */
+#define PKTMEM_RX_START PKTMEM_TX_ENDP1 /* Followed by RX buffer */
+#define PKTMEM_RX_END (PKTMEM_END - 1) /* RX buffer goes to the end of SRAM */
+
+/* This is a helper pointer for accessing the contents of the Ethernet header */
+
+#define BUF ((struct uip_eth_hdr *)priv->dev.d_buf)
+
+/* Debug ********************************************************************/
+
+#ifdef CONFIG_ENCX24J600_REGDEBUG
+# define enc_wrdump(a,v) lowsyslog("ENCX24J600: %02x<-%04x\n", a, v);
+# define enc_rddump(a,v) lowsyslog("ENCX24J600: %02x->%04x\n", a, v);
+# define enc_bfsdump(a,m) lowsyslog("ENCX24J600: %02x|=%04x\n", a, m);
+# define enc_bfcdump(a,m) lowsyslog("ENCX24J600: %02x&=~%04x\n", a, m);
+# define enc_cmddump(c) lowsyslog("ENCX24J600: CMD: %02x\n", c);
+# define enc_bmdump(c,b,s) lowsyslog("ENCX24J600: CMD: %02x buffer: %p length: %d\n", c,b,s);
+#else
+# define enc_wrdump(a,v)
+# define enc_rddump(a,v)
+# define enc_bfsdump(a,m)
+# define enc_bfcdump(a,m)
+# define enc_cmddump(c)
+# define enc_bmdump(c,b,s)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The state of the interface */
+
+enum enc_state_e
+{
+ ENCSTATE_UNINIT = 0, /* The interface is in an uninitialized state */
+ ENCSTATE_DOWN, /* The interface is down */
+ ENCSTATE_UP /* The interface is up */
+};
+
+/* The enc_driver_s encapsulates all state information for a single hardware
+ * interface
+ */
+
+struct enc_driver_s
+{
+ /* Device control */
+
+ uint8_t ifstate; /* Interface state: See ENCSTATE_* */
+ uint8_t bank; /* Currently selected bank command */
+ uint16_t nextpkt; /* Next packet address */
+ FAR const struct enc_lower_s *lower; /* Low-level MCU-specific support */
+
+ /* Timing */
+
+ WDOG_ID txpoll; /* TX poll timer */
+ WDOG_ID txtimeout; /* TX timeout timer */
+
+ /* If we don't own the SPI bus, then we cannot do SPI accesses from the
+ * interrupt handler.
+ */
+
+ struct work_s irqwork; /* Interrupt continuation work queue support */
+ struct work_s towork; /* Tx timeout work queue support */
+ struct work_s pollwork; /* Poll timeout work queue support */
+
+ /* This is the contained SPI driver intstance */
+
+ FAR struct spi_dev_s *spi;
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct uip_driver_s dev; /* Interface understood by uIP */
+
+ /* Statistics */
+
+#ifdef CONFIG_ENCX24J600_STATS
+ struct enc_stats_s stats;
+#endif
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct enc_driver_s g_encx24j600[CONFIG_ENCX24J600_NINTERFACES];
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Low-level SPI helpers */
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void enc_configspi(FAR struct spi_dev_s *spi);
+# define enc_lock(priv);
+# define enc_unlock(priv);
+#else
+# define enc_configspi(spi)
+static void enc_lock(FAR struct enc_driver_s *priv);
+static inline void enc_unlock(FAR struct enc_driver_s *priv);
+#endif
+
+/* SPI control register access */
+
+static inline void enc_setethrst(FAR struct enc_driver_s *priv);
+static void enc_setbank(FAR struct enc_driver_s *priv, uint8_t bank);
+static uint16_t enc_rdreg(FAR struct enc_driver_s *priv, uint16_t ctrlreg);
+static void enc_wrreg(FAR struct enc_driver_s *priv, uint16_t ctrlreg,
+ uint16_t wrdata);
+static int enc_waitreg(FAR struct enc_driver_s *priv, uint16_t ctrlreg,
+ uint16_t bits, uint16_t value);
+static void enc_bfs(FAR struct enc_driver_s *priv, uint16_t ctrlreg,
+ uint16_t bits);
+static void enc_bfc(FAR struct enc_driver_s *priv, uint16_t ctrlreg,
+ uint16_t bits);
+static void enc_cmd(FAR struct enc_driver_s *priv, uint8_t cmd, uint16_t arg);
+
+#if 0 /* Sometimes useful */
+static void enc_rxdump(FAR struct enc_driver_s *priv);
+static void enc_txdump(FAR struct enc_driver_s *priv);
+#endif
+
+/* SPI buffer transfers */
+
+static void enc_rdbuffer(FAR struct enc_driver_s *priv, FAR uint8_t *buffer,
+ size_t buflen);
+static inline void enc_wrbuffer(FAR struct enc_driver_s *priv,
+ FAR const uint8_t *buffer, size_t buflen);
+
+/* PHY register access */
+
+static uint16_t enc_rdphy(FAR struct enc_driver_s *priv, uint8_t phyaddr);
+static void enc_wrphy(FAR struct enc_driver_s *priv, uint8_t phyaddr,
+ uint16_t phydata);
+
+/* Common TX logic */
+
+static int enc_transmit(FAR struct enc_driver_s *priv);
+static int enc_uiptxpoll(struct uip_driver_s *dev);
+
+/* Interrupt handling */
+
+static void enc_linkstatus(FAR struct enc_driver_s *priv);
+static void enc_txif(FAR struct enc_driver_s *priv);
+static void enc_rxdispatch(FAR struct enc_driver_s *priv);
+static void enc_pktif(FAR struct enc_driver_s *priv);
+static void enc_irqworker(FAR void *arg);
+static int enc_interrupt(int irq, FAR void *context);
+
+/* Watchdog timer expirations */
+
+static void enc_toworker(FAR void *arg);
+static void enc_txtimeout(int argc, uint32_t arg, ...);
+static void enc_pollworker(FAR void *arg);
+static void enc_polltimer(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int enc_ifup(struct uip_driver_s *dev);
+static int enc_ifdown(struct uip_driver_s *dev);
+static int enc_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int enc_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+static int enc_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+/* Initialization */
+
+static void enc_pwrsave(FAR struct enc_driver_s *priv);
+static void enc_setmacaddr(FAR struct enc_driver_s *priv);
+static int enc_reset(FAR struct enc_driver_s *priv);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: enc_configspi
+ *
+ * Description:
+ * Configure the SPI for use with the ENCX24J600
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_OWNBUS
+static inline void enc_configspi(FAR struct spi_dev_s *spi)
+{
+ /* Configure SPI for the ENCX24J600. But only if we own the SPI bus.
+ * Otherwise, don't bother because it might change.
+ */
+
+ SPI_SETMODE(spi, CONFIG_ENCX24J600_SPIMODE);
+ SPI_SETBITS(spi, 8);
+ SPI_SETFREQUENCY(spi, CONFIG_ENCX24J600_FREQUENCY)
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_lock
+ *
+ * Description:
+ * Select the SPI, locking and re-configuring if necessary
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SPI_OWNBUS
+static void enc_lock(FAR struct enc_driver_s *priv)
+{
+ /* Lock the SPI bus in case there are multiple devices competing for the SPI
+ * bus.
+ */
+
+ SPI_LOCK(priv->spi, true);
+
+ /* Now make sure that the SPI bus is configured for the ENCX24J600 (it
+ * might have gotten configured for a different device while unlocked)
+ */
+
+ SPI_SETMODE(priv->spi, CONFIG_ENCX24J600_SPIMODE);
+ SPI_SETBITS(priv->spi, 8);
+ SPI_SETFREQUENCY(priv->spi, CONFIG_ENCX24J600_FREQUENCY);
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_unlock
+ *
+ * Description:
+ * De-select the SPI
+ *
+ * Parameters:
+ * spi - Reference to the SPI driver structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_SPI_OWNBUS
+static inline void enc_unlock(FAR struct enc_driver_s *priv)
+{
+ /* Relinquish the lock on the bus. */
+
+ SPI_LOCK(priv->spi, false);
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_cmd
+ *
+ * Description:
+ * Execute two byte command.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * cmd - ENCX24J600 two-byte command
+ * arg - Two byte argument to the command
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_cmd(FAR struct enc_driver_s *priv, uint8_t cmd, uint16_t arg)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENCX24J600 chip */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, true);;
+
+ (void)SPI_SEND(priv->spi, cmd); /* Clock out the command */
+ (void)SPI_SEND(priv->spi, arg & 0xff); /* clock out the low byte */
+ (void)SPI_SEND(priv->spi, arg >> 8); /* clock out the high byte */
+
+ /* De-select ENCX24J600 chip. */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, false);
+ enc_wrdump(cmd, arg);
+}
+
+/****************************************************************************
+ * Function: enc_setethrst
+ *
+ * Description:
+ * Issues System Reset by setting ETHRST (ECON2<4>)
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline void enc_setethrst(FAR struct enc_driver_s *priv)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENC28J60 chip */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, true);;
+
+ /* Send the system reset command. */
+
+ (void)SPI_SEND(priv->spi, ENC_SETETHRST);
+
+ up_udelay(25);
+
+ /* De-select ENC28J60 chip. */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, false);
+ enc_cmddump(ENC_SETETHRST);
+}
+
+/****************************************************************************
+ * Function: enc_setbank
+ *
+ * Description:
+ * Set the bank for the next control register access.
+ *
+ * Assumption:
+ * The caller has exclusive access to the SPI bus
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * bank - SPI command to select the bank with
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * The chip is selected and SPI is ready for communication.
+ *
+ ****************************************************************************/
+
+static void enc_setbank(FAR struct enc_driver_s *priv, uint8_t bank)
+{
+
+ /* Check if a bank has to be set and if the bank setting has changed.
+ * For registers that are available on all banks, the bank command is set to 0.
+ */
+
+ if (bank != 0 && bank != priv->bank)
+ {
+ /* Select bank with supplied command */
+
+ SPI_SEND(priv->spi, bank);
+
+ /* Then remember the bank setting */
+
+ priv->bank = bank;
+ }
+}
+
+/****************************************************************************
+ * Function: enc_rdreg
+ *
+ * Description:
+ * Read one word from a control register using the RCR command.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ctrlreg - Bit encoded address of banked register to read
+ *
+ * Returned Value:
+ * The byte read from the banked register
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint16_t enc_rdreg(FAR struct enc_driver_s *priv, uint16_t ctrlreg)
+{
+ uint16_t rddata;
+
+ DEBUGASSERT(priv && priv->spi);
+ DEBUGASSERT((ctrlreg & 0xe0) == 0); /* banked regeitsers only */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, true);
+
+ enc_setbank(priv, GETBANK(ctrlreg));
+
+ SPI_SEND(priv->spi, ENC_RCR | GETADDR(ctrlreg));
+
+ rddata = SPI_SEND(priv->spi, 0); /* clock in the low byte */
+ rddata |= SPI_SEND(priv->spi, 0) << 8; /* clock in the high byte */
+
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, false);
+ enc_rddump(GETADDR(ctrlreg), rddata);
+
+ return rddata;
+}
+
+/****************************************************************************
+ * Function: enc_wrreg
+ *
+ * Description:
+ * Write one word to a control register using the WCR command.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ctrlreg - Bit encoded address of banked register to write
+ * wrdata - The data to send
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_wrreg(FAR struct enc_driver_s *priv, uint16_t ctrlreg,
+ uint16_t wrdata)
+{
+ DEBUGASSERT(priv && priv->spi);
+ DEBUGASSERT((ctrlreg & 0xe0) == 0); /* banked regeitsers only */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, true);;
+
+ enc_setbank(priv, GETBANK(ctrlreg));
+
+ SPI_SEND(priv->spi, ENC_WCR | GETADDR(ctrlreg));
+ SPI_SEND(priv->spi, wrdata & 0xff); /* clock out the low byte */
+ SPI_SEND(priv->spi, wrdata >> 8); /* clock out the high byte */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, false);
+ enc_wrdump(GETADDR(ctrlreg), wrdata);
+}
+
+/****************************************************************************
+ * Function: enc_waitbreg
+ *
+ * Description:
+ * Wait until banked register bit(s) take a specific value (or a timeout
+ * occurs).
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ctrlreg - Bit encoded address of banked register to check
+ * bits - The bits to check (a mask)
+ * value - The value of the bits to return (value under mask)
+ *
+ * Returned Value:
+ * OK on success, negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_waitreg(FAR struct enc_driver_s *priv, uint16_t ctrlreg,
+ uint16_t bits, uint16_t value)
+{
+ uint32_t start = clock_systimer();
+ uint32_t elapsed;
+ uint16_t rddata;
+
+ /* Loop until the exit condition is met */
+
+ do
+ {
+ /* Read the byte from the requested banked register */
+
+ rddata = enc_rdreg(priv, ctrlreg);
+ elapsed = clock_systimer() - start;
+ }
+ while ((rddata & bits) != value || elapsed > ENC_POLLTIMEOUT);
+
+ return (rddata & bits) == value ? OK : -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: enc_bfs
+ *
+ * Description:
+ * Bit Field Set.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ctrlreg - Bit encoded address of banked register to set bits in
+ * bits - The bits to set (a mask)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_bfs(FAR struct enc_driver_s *priv, uint16_t ctrlreg,
+ uint16_t bits)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENCX24J600 chip */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, true);;
+
+ /* Set the bank */
+
+ enc_setbank(priv, GETBANK(ctrlreg));
+
+ /* Send the BFS command and data. The sequence requires 24-clocks:
+ * 8 to clock out the cmd + 16 to clock out the data.
+ */
+
+ (void)SPI_SEND(priv->spi, ENC_BFS | GETADDR(ctrlreg)); /* Clock out the command */
+ (void)SPI_SEND(priv->spi, bits & 0xff); /* clock out the low byte */
+ (void)SPI_SEND(priv->spi, bits >> 8); /* clock out the high byte */
+
+ /* De-select ENCX24J600 chip. */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, false);
+ enc_bfsdump(GETADDR(ctrlreg), bits);
+}
+
+/****************************************************************************
+ * Function: enc_bfc
+ *
+ * Description:
+ * Bit Field Clear.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * ctrlreg - Bit encoded address of banked register to clear bits in
+ * bits - The bits to clear (a mask)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_bfc(FAR struct enc_driver_s *priv, uint16_t ctrlreg,
+ uint16_t bits)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENCX24J600 chip */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, true);;
+
+ /* Set the bank */
+
+ enc_setbank(priv, GETBANK(ctrlreg));
+
+ /* Send the BFC command and data. The sequence requires 24-clocks:
+ * 8 to clock out the cmd + 16 to clock out the data.
+ */
+
+ (void)SPI_SEND(priv->spi, ENC_BFC | GETADDR(ctrlreg)); /* Clock out the command */
+ (void)SPI_SEND(priv->spi, bits & 0xff); /* clock out the low byte */
+ (void)SPI_SEND(priv->spi, bits >> 8); /* clock out the high byte */
+
+ /* De-select ENCX24J600 chip. */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, false);
+ enc_bfcdump(GETADDR(ctrlreg), bits);
+}
+
+/****************************************************************************
+ * Function: enc_txdump enc_rxdump
+ *
+ * Description:
+ * Dump registers associated with receiving or sending packets.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if 0 /* Sometimes useful */
+static void enc_rxdump(FAR struct enc_driver_s *priv)
+{
+ lowsyslog("Rx Registers:\n");
+ lowsyslog(" EIE: %02x EIR: %02x\n",
+ enc_rdgreg(priv, ENC_EIE), enc_rdgreg(priv, ENC_EIR));
+ lowsyslog(" ESTAT: %02x ECON1: %02x ECON2: %02x\n",
+ enc_rdgreg(priv, ENC_ESTAT), enc_rdgreg(priv, ENC_ECON1),
+ enc_rdgreg(priv, ENC_ECON2));
+ lowsyslog(" ERXST: %02x %02x\n",
+ enc_rdbreg(priv, ENC_ERXSTH), enc_rdbreg(priv, ENC_ERXSTL));
+ lowsyslog(" ERXND: %02x %02x\n",
+ enc_rdbreg(priv, ENC_ERXNDH), enc_rdbreg(priv, ENC_ERXNDL));
+ lowsyslog(" ERXRDPT: %02x %02x\n",
+ enc_rdbreg(priv, ENC_ERXRDPTH), enc_rdbreg(priv, ENC_ERXRDPTL));
+ lowsyslog(" ERXFCON: %02x EPKTCNT: %02x\n",
+ enc_rdbreg(priv, ENC_ERXFCON), enc_rdbreg(priv, ENC_EPKTCNT));
+ lowsyslog(" MACON1: %02x MACON3: %02x\n",
+ enc_rdbreg(priv, ENC_MACON1), enc_rdbreg(priv, ENC_MACON3));
+ lowsyslog(" MAMXFL: %02x %02x\n",
+ enc_rdbreg(priv, ENC_MAMXFLH), enc_rdbreg(priv, ENC_MAMXFLL));
+ lowsyslog(" MAADR: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ enc_rdbreg(priv, ENC_MAADR1), enc_rdbreg(priv, ENC_MAADR2),
+ enc_rdbreg(priv, ENC_MAADR3), enc_rdbreg(priv, ENC_MAADR4),
+ enc_rdbreg(priv, ENC_MAADR5), enc_rdbreg(priv, ENC_MAADR6));
+}
+#endif
+
+#if 0 /* Sometimes useful */
+static void enc_txdump(FAR struct enc_driver_s *priv)
+{
+ lowsyslog("Tx Registers:\n");
+ lowsyslog(" EIE: %02x EIR: %02x ESTAT: %02x\n",
+ enc_rdgreg(priv, ENC_EIE), enc_rdgreg(priv, ENC_EIR),);
+ lowsyslog(" ESTAT: %02x ECON1: %02x\n",
+ enc_rdgreg(priv, ENC_ESTAT), enc_rdgreg(priv, ENC_ECON1));
+ lowsyslog(" ETXST: %02x %02x\n",
+ enc_rdbreg(priv, ENC_ETXSTH), enc_rdbreg(priv, ENC_ETXSTL));
+ lowsyslog(" ETXND: %02x %02x\n",
+ enc_rdbreg(priv, ENC_ETXNDH), enc_rdbreg(priv, ENC_ETXNDL));
+ lowsyslog(" MACON1: %02x MACON3: %02x MACON4: %02x\n",
+ enc_rdbreg(priv, ENC_MACON1), enc_rdbreg(priv, ENC_MACON3),
+ enc_rdbreg(priv, ENC_MACON4));
+ lowsyslog(" MACON1: %02x MACON3: %02x MACON4: %02x\n",
+ enc_rdbreg(priv, ENC_MACON1), enc_rdbreg(priv, ENC_MACON3),
+ enc_rdbreg(priv, ENC_MACON4));
+ lowsyslog(" MABBIPG: %02x MAIPG %02x %02x\n",
+ enc_rdbreg(priv, ENC_MABBIPG), enc_rdbreg(priv, ENC_MAIPGH),
+ enc_rdbreg(priv, ENC_MAIPGL));
+ lowsyslog(" MACLCON1: %02x MACLCON2: %02x\n",
+ enc_rdbreg(priv, ENC_MACLCON1), enc_rdbreg(priv, ENC_MACLCON2));
+ lowsyslog(" MAMXFL: %02x %02x\n",
+ enc_rdbreg(priv, ENC_MAMXFLH), enc_rdbreg(priv, ENC_MAMXFLL));
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_rdbuffer
+ *
+ * Description:
+ * Read a buffer of data from RX Data Buffer.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * buffer - A pointer to the buffer to read into
+ * buflen - The number of bytes to read
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * RX Data pointer is set to the correct address
+ *
+ ****************************************************************************/
+
+static void enc_rdbuffer(FAR struct enc_driver_s *priv, FAR uint8_t *buffer,
+ size_t buflen)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ /* Select ENCX24J600 chip */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, true);
+
+ /* Send the read buffer memory command (ignoring the response) */
+
+ (void)SPI_SEND(priv->spi, ENC_RRXDATA);
+
+ /* Then read the buffer data */
+
+ SPI_RECVBLOCK(priv->spi, buffer, buflen);
+
+ /* De-select ENCX24J600 chip. */
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, false);
+ enc_bmdump(ENC_RRXDATA, buffer, buflen);
+}
+
+/****************************************************************************
+ * Function: enc_wrbuffer
+ *
+ * Description:
+ * Write a buffer of data.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * buffer - A pointer to the buffer to write from
+ * buflen - The number of bytes to write
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * General Purpose Write pointer is set to the correct address
+ *
+ ****************************************************************************/
+
+static inline void enc_wrbuffer(FAR struct enc_driver_s *priv,
+ FAR const uint8_t *buffer, size_t buflen)
+{
+ DEBUGASSERT(priv && priv->spi);
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, true);;
+
+ SPI_SEND(priv->spi, ENC_WGPDATA);
+ SPI_SNDBLOCK(priv->spi, buffer, buflen);
+
+ SPI_SELECT(priv->spi, SPIDEV_ETHERNET, false);
+ enc_bmdump(ENC_WGPDATA, buffer, buflen);
+}
+
+/****************************************************************************
+ * Function: enc_rdphy
+ *
+ * Description:
+ * Read 16-bits of PHY data.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * phyaddr - The PHY register address
+ *
+ * Returned Value:
+ * 16-bit value read from the PHY
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint16_t enc_rdphy(FAR struct enc_driver_s *priv, uint8_t phyaddr)
+{
+ uint16_t data = 0;
+
+ /* "To read from a PHY register:
+ * 1. Write the address of the PHY register to read from into the MIREGADR
+ * register (Register 3-1). Make sure to also set reserved bit 8 of this
+ * register.
+ */
+
+ enc_wrreg(priv, ENC_MIREGADR, phyaddr);
+
+ /* 2. Set the MIIRD bit (MICMD<0>, Register 3-2). The read operation begins
+ * and the BUSY bit (MISTAT<0>, Register 3-3) is automatically set by
+ * hardware.
+ */
+
+ enc_bfs(priv, ENC_MICMD, MICMD_MIIRD);
+
+ /* 3. Wait 25.6 μs. Poll the BUSY (MISTAT<0>) bit to be certain that the
+ * operation is complete. While busy, the host controller should not
+ * start any MIISCAN operations or write to the MIWR register. When the
+ * MAC has obtained the register contents, the BUSY bit will clear
+ * itself.
+ */
+
+ up_udelay(26);
+ if (enc_waitreg(priv, ENC_MISTAT, MISTAT_BUSY, 0x00) == OK)
+ {
+ /* 4. Clear the MIIRD (MICMD<0>) bit. */
+
+ enc_bfc(priv, ENC_MICMD, MICMD_MIIRD);
+
+ /* 5. Read the desired data from the MIRD register. For 8-bit interfaces,
+ * the order that these bytes are read is unimportant."
+ */
+
+ data = enc_rdreg(priv, ENC_MIRD);
+ }
+
+ return data;
+}
+
+/****************************************************************************
+ * Function: enc_wrphy
+ *
+ * Description:
+ * write 16-bits of PHY data.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ * phyaddr - The PHY register address
+ * phydata - 16-bit data to write to the PHY
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_wrphy(FAR struct enc_driver_s *priv, uint8_t phyaddr,
+ uint16_t phydata)
+{
+ /* "To write to a PHY register:
+ *
+ * 1. Write the address of the PHY register to write to into the MIREGADR
+ * register. Make sure to also set reserved bit 8 of this register.
+ */
+
+ enc_wrreg(priv, ENC_MIREGADR, 0x0100 | phyaddr);
+
+ /* 2. Write the 16 bits of data into the MIWR register. The low byte must
+ * be written first, followed by the high byte.
+ */
+
+ enc_wrreg(priv, ENC_MIWR, phydata);
+
+ /* 3. Writing to the high byte of MIWR begins the MIIM transaction and the
+ * BUSY (MISTAT<0>) bit is automatically set by hardware.
+ *
+ * The PHY register is written after the MIIM operation completes, which takes
+ * 25.6 μs. When the write operation has completed, the BUSY bit clears
+ * itself. The host controller should not start any MIISCAN, MIWR or MIIRD
+ * operations while the BUSY bit is set.
+ */
+
+ up_udelay(26);
+ enc_waitreg(priv, ENC_MISTAT, MISTAT_BUSY, 0);
+}
+
+/****************************************************************************
+ * Function: enc_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from:
+ *
+ * - pkif interrupt when an application responds to the receipt of data
+ * by trying to send something, or
+ * - From watchdog based polling.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_transmit(FAR struct enc_driver_s *priv)
+{
+ /* Increment statistics */
+
+ nllvdbg("Sending packet, pktlen: %d\n", priv->dev.d_len);
+
+#ifdef CONFIG_ENCX24J600_STATS
+ priv->stats.txrequests++;
+#endif
+
+ /* Verify that the hardware is ready to send another packet. The driver
+ * starts a transmission process by setting ECON1.TXRTS. When the packet is
+ * finished transmitting or is aborted due to an error/cancellation, the
+ * ECON1.TXRTS bit will be cleared.
+ *
+ * NOTE: If we got here, then we have committed to sending a packet.
+ * higher level logic must have assured that (1) there is no transmission
+ * in progress, and that (2) TX-related interrupts are disabled.
+ */
+
+ DEBUGASSERT((enc_rdreg(priv, ENC_ECON1) & ECON1_TXRTS) == 0);
+
+ /* Send the packet: address=priv->dev.d_buf, length=priv->dev.d_len */
+
+ enc_dumppacket("Transmit Packet", priv->dev.d_buf, priv->dev.d_len);
+
+ /* copy the packet into the transmit buffer */
+
+ enc_cmd(priv, ENC_WGPWRPT, PKTMEM_TX_START);
+ enc_wrbuffer(priv, priv->dev.d_buf, priv->dev.d_len);
+
+ /* Set TX Len registers. TX Start is set in enc_reset */
+
+ enc_wrreg(priv, ENC_ETXLEN, priv->dev.d_len);
+
+
+ /* Set TXRTS to send the packet in the transmit buffer */
+
+ enc_bfs(priv, ENC_ECON1, ECON1_TXRTS);
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer). Note:
+ * Is there a race condition. Could the TXIF interrupt occur before
+ * the timer is started?
+ */
+
+ (void)wd_start(priv->txtimeout, ENC_TXTIMEOUT, enc_txtimeout, 1, (uint32_t)priv);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: enc_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete,
+ * 2. When the preceding TX packet send timedout and the interface is reset
+ * 3. During normal TX polling
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * Interrupts are enabled but the caller holds the uIP lock.
+ *
+ ****************************************************************************/
+
+static int enc_uiptxpoll(struct uip_driver_s *dev)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_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.
+ */
+
+ nllvdbg("Poll result: d_len=%d\n", priv->dev.d_len);
+ if (priv->dev.d_len > 0)
+ {
+ uip_arp_out(&priv->dev);
+ enc_transmit(priv);
+
+ /* Stop the poll now because we can queue only one packet */
+
+ return -EBUSY;
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: enc_linkstatus
+ *
+ * Description:
+ * The current link status can be obtained from the PHSTAT1.LLSTAT or
+ * PHSTAT2.LSTAT.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_linkstatus(FAR struct enc_driver_s *priv)
+{
+ uint16_t regval;
+
+ /* Before transmitting the first packet after link establishment or
+ * auto-negotiation, the MAC duplex configuration must be manually set to
+ * match the duplex configuration of the PHY. To do this, configure
+ * FULDPX (MACON2<0>) to match PHYDPX (ESTAT<10>).
+ */
+
+ regval = enc_rdphy(priv, ENC_ESTAT);
+
+ if (regval & ESTAT_PHYDPX)
+ {
+ /* configure full-duplex */
+
+ enc_wrreg(priv, ENC_MABBIPG, 0x15);
+ enc_bfs(priv, ENC_MACON2, MACON2_FULDPX);
+ }
+ else
+ {
+ /* configure half-duplex */
+
+ enc_wrreg(priv, ENC_MABBIPG, 0x12);
+ enc_bfc(priv, ENC_MACON2, MACON2_FULDPX);
+ }
+}
+
+/****************************************************************************
+ * Function: enc_txif
+ *
+ * Description:
+ * An TXIF interrupt was received indicating that the last TX packet(s) is
+ * done
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Interrupts are enabled but the caller holds the uIP lock.
+ *
+ ****************************************************************************/
+
+static void enc_txif(FAR struct enc_driver_s *priv)
+{
+ /* If no further xmits are pending, then cancel the TX timeout */
+
+ wd_cancel(priv->txtimeout);
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&priv->dev, enc_uiptxpoll);
+}
+
+/****************************************************************************
+ * Function: enc_rxdispatch
+ *
+ * Description:
+ * Give the newly received packet to uIP.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Interrupts are enabled but the caller holds the uIP lock.
+ *
+ ****************************************************************************/
+
+static void enc_rxdispatch(FAR struct enc_driver_s *priv)
+{
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ nllvdbg("IP packet received (%02x)\n", BUF->type);
+ uip_arp_ipin(&priv->dev);
+ uip_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)
+ {
+ uip_arp_out(&priv->dev);
+ enc_transmit(priv);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP))
+ {
+ nllvdbg("ARP packet received (%02x)\n", BUF->type);
+ uip_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)
+ {
+ enc_transmit(priv);
+ }
+ }
+ else
+ {
+ nlldbg("Unsupported packet type dropped (%02x)\n", htons(BUF->type));
+ }
+}
+
+/****************************************************************************
+ * Function: enc_pktif
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Interrupts are enabled but the caller holds the uIP lock.
+ *
+ ****************************************************************************/
+
+static void enc_pktif(FAR struct enc_driver_s *priv)
+{
+ uint8_t rsv[8];
+ uint16_t pktlen;
+ uint32_t rxstat;
+
+ DEBUGASSERT(priv->nextpkt >= PKTMEM_RX_START && priv->nextpkt <= PKTMEM_RX_END);
+
+ /* Set the rx data pointer to the start of the received packet (ERXRDPT) */
+
+ enc_cmd(priv, ENC_WRXRDPT, priv->nextpkt);
+
+ /* Read the next packet pointer and the 6 byte read status vector (RSV)
+ * at the beginning of the received packet. (ERXRDPT should auto-increment
+ * and wrap to the beginning of the read buffer as necessary)
+ */
+
+ enc_rdbuffer(priv, rsv, 8);
+
+ /* Decode the new next packet pointer, and the RSV. The
+ * RSV is encoded as:
+ *
+ * Bits 0-15: Indicates length of the received frame. This includes the
+ * destination address, source address, type/length, data,
+ * padding and CRC fields. This field is stored in little-
+ * endian format.
+ * Bits 16-47: Bit encoded RX status.
+ */
+
+ priv->nextpkt = (uint16_t)rsv[1] << 8 | (uint16_t)rsv[0];
+ pktlen = (uint16_t)rsv[3] << 8 | (uint16_t)rsv[2];
+ rxstat = (uint32_t)rsv[7] << 24 | (uint32_t)rsv[6] << 16 |
+ (uint32_t)rsv[5] << 8 | (uint32_t)rsv[4];
+
+ nllvdbg("Receiving packet, nextpkt: %04x pktlen: %d rxstat: %08x\n",
+ priv->nextpkt, pktlen, rxstat);
+
+ /* Check if the packet was received OK */
+
+ if ((rxstat & RXSTAT_OK) == 0)
+ {
+ nlldbg("ERROR: RXSTAT: %08x\n", rxstat);
+
+#ifdef CONFIG_ENCX24J600_STATS
+ priv->stats.rxnotok++;
+#endif
+ }
+
+ /* Check for a usable packet length (4 added for the CRC) */
+
+ else if (pktlen > (CONFIG_NET_BUFSIZE + 4) || pktlen <= (UIP_LLH_LEN + 4))
+ {
+ nlldbg("Bad packet size dropped (%d)\n", pktlen);
+#ifdef CONFIG_ENCX24J600_STATS
+ priv->stats.rxpktlen++;
+#endif
+ }
+
+ /* Otherwise, read and process the packet */
+
+ else
+ {
+ /* Save the packet length (without the 4 byte CRC) in priv->dev.d_len */
+
+ priv->dev.d_len = pktlen - 4;
+
+ /* Copy the data data from the receive buffer to priv->dev.d_buf.
+ * ERXRDPT should be correctly positioned from the last call to to
+ * enc_rdbuffer (above).
+ */
+
+ enc_rdbuffer(priv, priv->dev.d_buf, priv->dev.d_len);
+ enc_dumppacket("Received Packet", priv->dev.d_buf, priv->dev.d_len);
+
+ /* Dispatch the packet to uIP */
+
+ enc_rxdispatch(priv);
+ }
+
+ /* Once the whole frame has been processed, the final value of ERXTAIL should
+ * be equal to (NextPacketPointer - 2).
+ */
+
+ /* @TODO check if no special handling needed (skip odd addresses?) */
+ enc_wrreg(priv, ENC_ERXTAIL, priv->nextpkt - 2);
+
+ /* Decrement the packet counter indicate we are done with this packet */
+
+ enc_bfs(priv, ENC_ECON1, ECON1_PKTDEC);
+
+}
+
+/****************************************************************************
+ * Function: enc_irqworker
+ *
+ * Description:
+ * Perform interrupt handling logic outside of the interrupt handler (on
+ * the work queue thread).
+ *
+ * Parameters:
+ * arg - The reference to the driver structure (case to void*)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_irqworker(FAR void *arg)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
+ uip_lock_t lock;
+ uint16_t eir;
+
+ DEBUGASSERT(priv);
+
+ /* Get exclusive access to both uIP and the SPI bus. */
+
+ lock = uip_lock();
+ enc_lock(priv);
+
+ /* A good practice is for the host controller to clear the Global Interrupt
+ * Enable bit, INTIE (EIE<15>), immediately after an interrupt event. This
+ * causes the interrupt pin to return to the non-asserted (high) state. Once
+ * the interrupt has been serviced, the INTIE bit is set again to re-enable
+ * interrupts. If a new interrupt occurs while servicing another, the act of
+ * resetting the global enable bit will cause a new falling edge to occur on
+ * the interrupt pin and ensure that the host does not miss any events
+ */
+
+ enc_bfc(priv, ENC_EIE, EIE_INTIE);
+
+ /* Loop until all interrupts have been processed (EIR==0). Note that
+ * there is no infinite loop check... if there are always pending interrupts,
+ * we are just broken.
+ */
+
+ while ((eir = enc_rdreg(priv, ENC_EIR) & EIR_ALLINTS) != 0)
+ {
+ /* Handle interrupts according to interrupt register register bit
+ * settings.
+ */
+
+ nllvdbg("EIR: %04x\n", eir);
+
+ if ((eir & EIR_DMAIF) != 0) /* DMA interrupt */
+ {
+ /* Not used by this driver. Just clear the interrupt request. */
+
+ enc_bfc(priv, ENC_EIR, EIR_DMAIF);
+ }
+
+ /* LINKIF: The link change interrupt occurs when the PHY link status
+ * changes. This flag is set by hardware when a link has either been
+ * established or broken between the device and a remote Ethernet partner.
+ * The current link status can be read from PHYLNK (ESTAT<8>). The
+ * interrupt should be cleared by software once it has been serviced.
+ *
+ * To enable the link change interrupt, set LINKIE (EIE<11>).
+ */
+
+ if ((eir & EIR_LINKIF) != 0) /* PHY Link Status Change */
+ {
+ enc_linkstatus(priv); /* Get current link status */
+ enc_bfc(priv, ENC_EIR, EIR_LINKIF); /* Clear the LINKIF interrupt */
+ }
+
+ /* The transmit complete interrupt occurs when the transmission of a
+ * frame has ended (whether or not it was successful). This flag is set
+ * when TXRTS (ECON1<1>) is cleared. The interrupt should be cleared by
+ * software once it has been serviced.
+ */
+
+ if ((eir & EIR_TXIF) != 0) /* Transmit Done */
+ {
+ enc_txif(priv);
+ enc_bfc(priv, ENC_EIR, EIR_TXIF);
+ }
+
+ /* The received packet pending interrupt occurs when one or more frames
+ * have been received and are ready for software processing. This flag is
+ * set when the PKTCNT<7:0> (ESTAT<7:0>) bits are non-zero. This interrupt
+ * flag is read-only and will automatically clear when the PKTCNT bits are
+ * decremented to zero. For more details about receiving and processing
+ * incoming frames, refer to Section 9.0 "Transmitting and Receiving
+ * Packets".
+ *
+ * To enable the received packet pending interrupt, set PKTIE (EIE<6>).
+ * The corresponding interrupt flag is PKTIF (EIR<6>).
+ */
+
+ if ((eir & EIR_PKTIF) != 0 /* RX Packet Pending */
+ && (enc_rdreg(priv, ENC_ESTAT) & ESTAT_PKTCNT_MASK) != 0)
+ {
+ enc_pktif(priv); /* Handle packet receipt */
+
+ /* No clearing necessary, after PKTCNT == 0 the bit is automatically
+ * cleared. This means we will loop until all packets are processed.
+ */
+ }
+
+#ifdef CONFIG_ENCX24J600_STATS
+
+ /* The transmit abort interrupt occurs when the transmission of a frame
+ * has been aborted. An abort can occur for any of the following reasons:
+ *
+ * * Excessive collisions occurred as defined by the Retransmission
+ * Maximum, MAXRET<3:0> bits (MACLCON<3:0>), setting. If this occurs,
+ * the COLCNT bits (ETXSTAT<3:0>) will indicate the number of collisions
+ * that occurred.
+ *
+ * * A late collision occurred after 63 bytes were transmitted. If this
+ * occurs, LATECOL (ETXSTAT<10>) will be set.
+ *
+ * * The medium was busy and the packet was deferred. If this occurs,
+ * EXDEFER (ETXSTAT<8>) will be set.
+ *
+ * * The application aborted the transmission by clearing TXRTS
+ * (ECON1<1>).
+ *
+ * The interrupt should be cleared by software once it has been serviced.
+ * To enable the transmit abort interrupt, set TXABTIE (EIE<2>).
+ */
+
+ if ((eir & EIR_TXABTIF) != 0) /* Transmit Abort */
+ {
+ priv->stats.txerifs++;
+ enc_bfc(priv, ENC_EIR, EIR_TXABTIF); /* Clear the TXABTIF interrupt */
+ }
+
+ /* The receive abort interrupt occurs when the reception of a frame has
+ * been aborted. A frame being received is aborted when the Head Pointer
+ * attempts to overrun the Tail Pointer, or when the packet counter has
+ * reached FFh. In either case, the receive buffer is full and cannot fit
+ * the incoming frame, so the packet has been dropped. This interrupt does
+ * not occur when packets are dropped due to the receive filters rejecting
+ * a packet. The interrupt should be cleared by software once it has been
+ * serviced.
+ *
+ * To enable the receive abort interrupt, set RXABTIE (EIE<1>).
+ * The corresponding interrupt flag is RXABTIF (EIR<1>).
+ */
+
+ if ((eir & EIR_RXABTIF) != 0) /* Receive Abort */
+ {
+ priv->stats.rxerifs++;
+ enc_bfc(priv, ENC_EIR, EIR_RXABTIF); /* Clear the RXABTIF interrupt */
+ }
+#endif
+
+ }
+
+ /* Enable Ethernet interrupts */
+
+ enc_bfs(priv, ENC_EIE, EIE_INTIE);
+
+ /* Release lock on the SPI bus and uIP */
+
+ enc_unlock(priv);
+ uip_unlock(lock);
+
+ /* Enable GPIO interrupts */
+
+ priv->lower->enable(priv->lower);
+}
+
+/****************************************************************************
+ * Function: enc_interrupt
+ *
+ * Description:
+ * Hardware interrupt handler
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_interrupt(int irq, FAR void *context)
+{
+ register FAR struct enc_driver_s *priv = &g_encx24j600[0];
+
+ /* In complex environments, we cannot do SPI transfers from the interrupt
+ * handler because semaphores are probably used to lock the SPI bus. In
+ * this case, we will defer processing to the worker thread. This is also
+ * much kinder in the use of system resources and is, therefore, probably
+ * a good thing to do in any event.
+ */
+
+ DEBUGASSERT(work_available(&priv->irqwork));
+
+ /* Notice that further GPIO interrupts are disabled until the work is
+ * actually performed. This is to prevent overrun of the worker thread.
+ * Interrupts are re-enabled in enc_irqworker() when the work is completed.
+ */
+
+ priv->lower->disable(priv->lower);
+ return work_queue(HPWORK, &priv->irqwork, enc_irqworker, (FAR void *)priv, 0);
+}
+
+/****************************************************************************
+ * Function: enc_toworker
+ *
+ * Description:
+ * Our TX watchdog timed out. This is the worker thread continuation of
+ * the watchdog timer interrupt. Reset the hardware and start again.
+ *
+ * Parameters:
+ * arg - The reference to the driver structure (case to void*)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_toworker(FAR void *arg)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
+ uip_lock_t lock;
+ int ret;
+
+ nlldbg("Tx timeout\n");
+ DEBUGASSERT(priv);
+
+ /* Get exclusive access to both uIP and the SPI bus. */
+
+ lock = uip_lock();
+ enc_lock(priv);
+
+ /* Increment statistics and dump debug info */
+
+#ifdef CONFIG_ENCX24J600_STATS
+ priv->stats.txtimeouts++;
+#endif
+
+ /* Then reset the hardware: Take the interface down, then bring it
+ * back up
+ */
+
+ ret = enc_ifdown(&priv->dev);
+ DEBUGASSERT(ret == OK);
+ ret = enc_ifup(&priv->dev);
+ DEBUGASSERT(ret == OK);
+ (void)ret;
+
+ /* Then poll uIP for new XMIT data */
+
+ (void)uip_poll(&priv->dev, enc_uiptxpoll);
+
+ /* Release lock on the SPI bus and uIP */
+
+ enc_unlock(priv);
+ uip_unlock(lock);
+}
+
+/****************************************************************************
+ * Function: enc_txtimeout
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Perform work on the worker thread.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_txtimeout(int argc, uint32_t arg, ...)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
+ int ret;
+
+ /* In complex environments, we cannot do SPI transfers from the timout
+ * handler because semaphores are probably used to lock the SPI bus. In
+ * this case, we will defer processing to the worker thread. This is also
+ * much kinder in the use of system resources and is, therefore, probably
+ * a good thing to do in any event.
+ */
+
+ DEBUGASSERT(priv && work_available(&priv->towork));
+
+ /* Notice that Tx timeout watchdog is not active so further Tx timeouts
+ * can occur until we restart the Tx timeout watchdog.
+ */
+
+ ret = work_queue(HPWORK, &priv->towork, enc_toworker, (FAR void *)priv, 0);
+ (void)ret;
+ DEBUGASSERT(ret == OK);
+}
+
+/****************************************************************************
+ * Function: enc_pollworker
+ *
+ * Description:
+ * Periodic timer handler continuation.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_pollworker(FAR void *arg)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
+ uip_lock_t lock;
+
+ DEBUGASSERT(priv);
+
+ /* Get exclusive access to both uIP and the SPI bus. */
+
+ lock = uip_lock();
+ enc_lock(priv);
+
+ /* Verify that the hardware is ready to send another packet. The driver
+ * start a transmission process by setting ECON1.TXRTS. When the packet is
+ * finished transmitting or is aborted due to an error/cancellation, the
+ * ECON1.TXRTS bit will be cleared.
+ */
+
+ if ((enc_rdreg(priv, ENC_ECON1) & ECON1_TXRTS) == 0)
+ {
+ /* Yes.. update TCP timing states and poll uIP for new XMIT data. Hmmm..
+ * looks like a bug here to me. Does this mean if there is a transmit
+ * in progress, we will missing TCP time state updates?
+ */
+
+ (void)uip_timer(&priv->dev, enc_uiptxpoll, ENC_POLLHSEC);
+ }
+
+ /* Release lock on the SPI bus and uIP */
+
+ enc_unlock(priv);
+ uip_unlock(lock);
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(priv->txpoll, ENC_WDDELAY, enc_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * Function: enc_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_polltimer(int argc, uint32_t arg, ...)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
+ int ret;
+
+ /* In complex environments, we cannot do SPI transfers from the timout
+ * handler because semaphores are probably used to lock the SPI bus. In
+ * this case, we will defer processing to the worker thread. This is also
+ * much kinder in the use of system resources and is, therefore, probably
+ * a good thing to do in any event.
+ */
+
+ DEBUGASSERT(priv && work_available(&priv->pollwork));
+
+ /* Notice that poll watchdog is not active so further poll timeouts can
+ * occur until we restart the poll timeout watchdog.
+ */
+
+ ret = work_queue(HPWORK, &priv->pollwork, enc_pollworker, (FAR void *)priv, 0);
+ (void)ret;
+ DEBUGASSERT(ret == OK);
+}
+
+/****************************************************************************
+ * Function: enc_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 enc_ifup(struct uip_driver_s *dev)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+ int ret;
+
+ nlldbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Lock the SPI bus so that we have exclusive access */
+
+ enc_lock(priv);
+
+ /* Initialize Ethernet interface, set the MAC address, and make sure that
+ * the ENC28J80 is not in power save mode.
+ */
+
+ ret = enc_reset(priv);
+ if (ret == OK)
+ {
+
+ enc_setmacaddr(priv);
+ /* enc_pwrfull(priv); */
+
+
+ /* Enable interrupts at the ENCX24J600. Interrupts are still disabled
+ * at the interrupt controller.
+ */
+
+ enc_bfc(priv, ENC_EIR, EIR_ALLINTS);
+ enc_bfs(priv, ENC_EIE, EIE_INTIE | EIE_LINKIE |
+ EIE_PKTIE | EIE_RXABTIE |
+ EIE_TXIE );
+
+#ifdef CONFIG_ENCX24J600_STATS
+ enc_bfs(priv, ENC_EIE, EIE_TXABTIE);
+#endif
+
+ /* Enable the receiver */
+
+ enc_bfs(priv, ENC_ECON1, ECON1_RXEN);
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(priv->txpoll, ENC_WDDELAY, enc_polltimer, 1, (uint32_t)priv);
+
+ /* Mark the interface up and enable the Ethernet interrupt at the
+ * controller
+ */
+
+ priv->ifstate = ENCSTATE_UP;
+ priv->lower->enable(priv->lower);
+ }
+
+ /* Un-lock the SPI bus */
+
+ enc_unlock(priv);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Function: enc_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_ifdown(struct uip_driver_s *dev)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+ irqstate_t flags;
+ int ret;
+
+ nlldbg("Taking down: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24 );
+
+ /* Lock the SPI bus so that we have exclusive access */
+
+ enc_lock(priv);
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+ priv->lower->disable(priv->lower);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(priv->txpoll);
+ wd_cancel(priv->txtimeout);
+
+ /* Reset the device and leave in the power save state */
+
+ ret = enc_reset(priv);
+ enc_pwrsave(priv);
+
+ priv->ifstate = ENCSTATE_DOWN;
+ irqrestore(flags);
+
+ /* Un-lock the SPI bus */
+
+ enc_unlock(priv);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Function: enc_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 enc_txavail(struct uip_driver_s *dev)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ /* Lock the SPI bus so that we have exclusive access */
+
+ enc_lock(priv);
+
+ /* Ignore the notification if the interface is not yet up */
+
+ flags = irqsave();
+ if (priv->ifstate == ENCSTATE_UP)
+ {
+ /* Check if the hardware is ready to send another packet. The driver
+ * starts a transmission process by setting ECON1.TXRTS. When the packet is
+ * finished transmitting or is aborted due to an error/cancellation, the
+ * ECON1.TXRTS bit will be cleared.
+ */
+
+ if ((enc_rdreg(priv, ENC_ECON1) & ECON1_TXRTS) == 0)
+ {
+ /* The interface is up and TX is idle; poll uIP for new XMIT data */
+
+ (void)uip_poll(&priv->dev, enc_uiptxpoll);
+ }
+ }
+
+ /* Un-lock the SPI bus */
+
+ irqrestore(flags);
+ enc_unlock(priv);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: enc_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int enc_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+
+ /* Lock the SPI bus so that we have exclusive access */
+
+ enc_lock(priv);
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+
+ /* Un-lock the SPI bus */
+
+ enc_unlock(priv);
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int enc_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
+{
+ FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)dev->d_private;
+
+ /* Lock the SPI bus so that we have exclusive access */
+
+ enc_lock(priv);
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+#warning "Multicast MAC support not implemented"
+
+ /* Un-lock the SPI bus */
+
+ enc_unlock(priv);
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: enc_pwrsave
+ *
+ * Description:
+ * The ENCX24J600 may be placed in Power-Down mode through the command
+ * interface. In this mode, the device will no longer be able to transmit or
+ * receive any packets or perform DMA operations. However, most registers, and
+ * all buffer memories, retain their states and remain accessible by the host
+ * controller. The clock driver also remains operational, leaving the CLKOUT
+ * function unaffected. However, the MAC/MII and PHY registers all become
+ * inaccessible, and the PHY registers lose their current states.
+ *
+ * 1. Turn off the Modular Exponentiation and AES engines by clearing
+ * CRYPTEN (EIR<15>).
+ * 2. Turn off packet reception by clearing RXEN (ECON1<0>).
+ * 3. Wait for any in-progress receptions to complete by polling
+ * RXBUSY (ESTAT<13>) until it is clear.
+ * 4. Wait for any current transmission operation to complete by verifying
+ * that TXRTS (ECON1<1>) is clear.
+ * 5. Power-down the PHY by setting the PSLEEP bit (PHCON1<11>).
+ * 6. Power-down the Ethernet interface by clearing
+ * ETHEN and STRCH (ECON2<15,14>). Disabling the LED stretching behavior is
+ * necessary to ensure no LEDs get trapped in a perpetually illuminated
+ * state in the event they are being stretched on when ETHEN is cleared.
+ *
+ * Note:
+ * Instead of providing a powerup function, the job is done by enc_reset.
+ * enc_ifup calls it anyway.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_pwrsave(FAR struct enc_driver_s *priv)
+{
+ uint16_t regval;
+
+ nllvdbg("Set PWRSV\n");
+
+ /* 1. Turn off AES */
+
+ enc_bfc(priv, ENC_EIR, EIR_CRYPTEN);
+
+ /* 2. Turn off packet reception */
+
+ enc_bfc(priv, ENC_ECON1, ECON1_RXEN);
+
+ /* 3. Wait for pending reception to complete */
+
+ enc_waitreg(priv, ENC_ESTAT, ESTAT_RXBUSY, 0);
+
+ /* 4. Wait for any current transmissions to complete */
+
+ enc_waitreg(priv, ENC_ECON1, ECON1_TXRTS, 0);
+
+ /* 5. Power down the PHY */
+
+ regval = enc_rdphy(priv, ENC_PHCON1);
+ regval |= PHCON1_PSLEEP;
+ enc_wrphy(priv, ENC_PHCON1, regval);
+
+ /* 6. Power down the Ethernet interface */
+
+ enc_bfc(priv, ENC_ECON2, ECON2_ETHEN | ECON2_STRCH);
+}
+
+/****************************************************************************
+ * Function: enc_setmacaddr
+ *
+ * Description:
+ * Set the MAC address to the configured value. This is done after ifup
+ * or after a TX timeout. Note that this means that the interface must
+ * be down before configuring the MAC addr.
+ * If the MAC address is 0 in all digits, the ENCX24J600's MAC is read out.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void enc_setmacaddr(FAR struct enc_driver_s *priv)
+{
+ uint16_t regval;
+ uint8_t *mac = priv->dev.d_mac.ether_addr_octet;
+ struct ether_addr zmac;
+
+ memset(&zmac, 0, sizeof(zmac));
+
+ if (memcmp(&priv->dev.d_mac, &zmac, sizeof(zmac)) == 0)
+ {
+ /* No user defined MAC address. Read it from the device. */
+
+ nvdbg("Using ENCX24J600's built in MAC address\n");
+
+ regval = enc_rdreg(priv, ENC_MAADR1);
+ mac[0] = regval & 0xff;
+ mac[1] = regval >> 8;
+
+ regval = enc_rdreg(priv, ENC_MAADR2);
+ mac[2] = regval & 0xff;
+ mac[3] = regval >> 8;
+
+ regval = enc_rdreg(priv, ENC_MAADR3);
+ mac[4] = regval & 0xff;
+ mac[5] = regval >> 8;
+ }
+ else
+ {
+ /* There is a user defined mac address. Write it to the ENCXJ600 */
+
+ nvdbg("Using an user defined MAC address\n");
+
+ enc_wrreg(priv, ENC_MAADR1, (uint16_t)mac[1] << 8 | (uint16_t)mac[0]);
+ enc_wrreg(priv, ENC_MAADR2, (uint16_t)mac[3] << 8 | (uint16_t)mac[2]);
+ enc_wrreg(priv, ENC_MAADR3, (uint16_t)mac[5] << 8 | (uint16_t)mac[4]);
+ }
+}
+
+/****************************************************************************
+ * Function: enc_reset
+ *
+ * Description:
+ * Stop, reset, re-initialize, and restart the ENCX24J600. This is done
+ * initially, on ifup, and after a TX timeout.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int enc_reset(FAR struct enc_driver_s *priv)
+{
+ int ret;
+ uint16_t regval;
+
+ nlldbg("Reset\n");
+
+ /* configure SPI for the ENCX24J600 */
+
+ enc_configspi(priv->spi);
+
+ do
+ {
+ enc_wrreg(priv, ENC_EUDAST, 0x1234);
+ }
+ while (enc_rdreg(priv, ENC_EUDAST) != 0x1234);
+
+ /* wait for clock to become ready */
+
+ ret = enc_waitreg(priv, ENC_ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY);
+
+ if (ret != OK)
+ {
+ nlldbg("ERROR: encx24j600 clock failed to become ready\n");
+ return -ENODEV;
+ }
+
+ /* reset the ENCX24J600 */
+
+ enc_setethrst(priv);
+
+ /* check if EUDAST has been reset to 0 */
+
+ regval = enc_rdreg(priv, ENC_EUDAST);
+
+ if (regval != 0x0000)
+ {
+ nlldbg("ERROR: encx24j600 seems not to be reset properly\n");
+ return -ENODEV;
+ }
+
+ /**
+ * Wait at least 256 μs for the PHY registers and PHY status bits to become
+ * available.
+ */
+ up_udelay(256);
+
+ /* Initialize receive and transmit buffers */
+
+ priv->nextpkt = PKTMEM_RX_START;
+ enc_wrreg(priv, ENC_ERXST, PKTMEM_RX_START);
+ enc_wrreg(priv, ENC_ETXST, PKTMEM_TX_START);
+
+ /* Program the Tail Pointer, ERXTAIL, to the last even address of the buffer */
+
+ enc_wrreg(priv, ENC_ERXTAIL, PKTMEM_RX_END);
+
+ /* "Typically, when using auto-negotiation, users should write 0x05E1 to PHANA
+ * to advertise flow control capability."
+ */
+
+ enc_wrphy(priv, ENC_PHANA, PHANA_ADPAUS0 | PHANA_AD10FD | PHANA_AD10 |
+ PHANA_AD100FD | PHANA_AD100 | PHANA_ADIEEE0);
+
+ /* restart auto-negotiation */
+
+ enc_wrphy(priv, ENC_PHCON1, PHCON1_RENEG);
+
+ do
+ {
+ regval = enc_rdphy(priv, ENC_PHSTAT1);
+ }
+ while ((regval & PHSTAT1_ANDONE) != 0);
+
+ nlldbg("Auto-negotation completed\n");
+
+ enc_linkstatus(priv);
+
+ /* Set the maximum packet size which the controller will accept */
+
+ enc_wrreg(priv, ENC_MAMXFL, CONFIG_NET_BUFSIZE);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: enc_initialize
+ *
+ * Description:
+ * Initialize the Ethernet driver. The ENCX24J600 device is assumed to be
+ * in the post-reset state upon entry to this function.
+ *
+ * Parameters:
+ * spi - A reference to the platform's SPI driver for the ENCX24J600
+ * lower - The MCU-specific interrupt used to control low-level MCU
+ * functions (i.e., ENCX24J600 GPIO interrupts).
+ * devno - If more than one ENCX24J600 is supported, then this is the
+ * zero based number that identifies the ENCX24J600;
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+int enc_initialize(FAR struct spi_dev_s *spi,
+ FAR const struct enc_lower_s *lower,
+ unsigned int devno)
+{
+ FAR struct enc_driver_s *priv;
+
+ DEBUGASSERT(devno < CONFIG_ENCX24J600_NINTERFACES);
+ priv = &g_encx24j600[devno];
+
+ /* Initialize the driver structure */
+
+ memset(g_encx24j600, 0, CONFIG_ENCX24J600_NINTERFACES*sizeof(struct enc_driver_s));
+ priv->dev.d_ifup = enc_ifup; /* I/F up (new IP address) callback */
+ priv->dev.d_ifdown = enc_ifdown; /* I/F down callback */
+ priv->dev.d_txavail = enc_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ priv->dev.d_addmac = enc_addmac; /* Add multicast MAC address */
+ priv->dev.d_rmmac = enc_rmmac; /* Remove multicast MAC address */
+#endif
+ priv->dev.d_private = priv; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ priv->txpoll = wd_create(); /* Create periodic poll timer */
+ priv->txtimeout = wd_create(); /* Create TX timeout timer */
+ priv->spi = spi; /* Save the SPI instance */
+ priv->lower = lower; /* Save the low-level MCU interface */
+
+ /* The interface should be in the down state. However, this function is called
+ * too early in initalization to perform the ENCX24J600 reset in enc_ifdown. We
+ * are depending upon the fact that the application level logic will call enc_ifdown
+ * later to reset the ENCX24J600.
+ */
+
+ priv->ifstate = ENCSTATE_UNINIT;
+
+ /* Attach the interrupt to the driver (but don't enable it yet) */
+
+ if (lower->attach(lower, enc_interrupt))
+ {
+ /* We could not attach the ISR to the interrupt */
+
+ return -EAGAIN;
+ }
+
+ /* Power down the device */
+
+ enc_pwrsave(priv);
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ return netdev_register(&priv->dev);
+}
+
+/****************************************************************************
+ * Function: enc_stats
+ *
+ * Description:
+ * Return accumulated ENCX24J600 statistics. Statistics are cleared after
+ * being returned.
+ *
+ * Parameters:
+ * devno - If more than one ENCX24J600 is supported, then this is the
+ * zero based number that identifies the ENCX24J600;
+ * stats - The user-provided location to return the statistics.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ENCX24J600_STATS
+int enc_stats(unsigned int devno, struct enc_stats_s *stats)
+{
+ FAR struct enc_driver_s *priv ;
+ irqstate_t flags;
+
+ DEBUGASSERT(devno < CONFIG_ENCX24J600_NINTERFACES);
+ priv = &g_encx24j600[devno];
+
+ /* Disable the Ethernet interrupt */
+
+ flags = irqsave();
+ memcpy(stats, &priv->stats, sizeof(struct enc_stats_s));
+ memset(&priv->stats, 0, sizeof(struct enc_stats_s));
+ irqrestore(flags);
+ return OK;
+}
+#endif
+#endif /* CONFIG_NET && CONFIG_ENCX24J600_NET */
diff --git a/nuttx/drivers/net/encx24j600.h b/nuttx/drivers/net/encx24j600.h
new file mode 100644
index 000000000..f0343efba
--- /dev/null
+++ b/nuttx/drivers/net/encx24j600.h
@@ -0,0 +1,423 @@
+/****************************************************************************
+ * drivers/net/encx24j600.h
+ *
+ * Copyright (C) 2013 UVC Ingenieure. All rights reserved.
+ * Author: Max Holtberg <mh@uvc.de>
+ *
+ * References:
+ * - ENC424J600/624J600 Data Sheet, Stand-Alone 10/100 Ethernet Controller
+ * with SPI or Parallel Interface, DS39935C, 2010 Microchip Technology Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_NET_ENCX24J600_H
+#define __DRIVERS_NET_ENCX24J600_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* ENCX24J600 Commands ********************************************************/
+
+/* The SPI opcodes are divided into four families:
+ *
+ * Single Byte: Direct opcode instructions; designed for task-oriented SFR
+ * operations with no data returned
+ *
+ * Two-Byte: Direct opcode instruction; designed for SFR operation with byte
+ * data returned
+ *
+ * Three-Byte: Opcode with word length argument; includes read and write
+ * operations, designed for pointer manipulation with word length data returned
+ *
+ * N-Byte: Opcode with one or more bytes of argument; includes read and write
+ * operations designed for general memory space access with one or more bytes of
+ * data returned
+ */
+
+/* Single-Byte Instructions */
+
+/* Because all single byte instructions are fixed length with no optional
+ * parameters, it is possible to execute any instruction immediately following
+ * the execution of any single byte instruction without deasserting the chip
+ * select line in between.
+ */
+
+#define ENC_B0SEL (0xc0) /* Selects SFR Bank 0 */
+#define ENC_B1SEL (0xc2) /* Selects SFR Bank 1 */
+#define ENC_B2SEL (0xc4) /* Selects SFR Bank 2 */
+#define ENC_B3SEL (0xc6) /* Selects SFR Bank 3 */
+#define ENC_SETETHRST (0xca) /* Issues System Reset by setting ETHRST (ECON2<4>) */
+#define ENC_FCDISABLE (0xe0) /* Disables flow control (sets ECON1<7:6> = 00) */
+#define ENC_FCSINGLE (0xe2) /* Transmits a single pause frame (sets ECON1<7:6> = 01) */
+#define ENC_FCMULTIPLE (0xe4) /* Enables flow control with periodic pause frames (sets ECON1<7:6> = 10) */
+#define ENC_FCCLEAR (0xe6) /* Terminates flow control with a final pause frame (sets ECON1<7:6> = 11) */
+#define ENC_SETPKTDEC (0xcc) /* Decrements PKTCNT by setting PKTDEC (ECON1<8>) */
+#define ENC_DMASTOP (0xd2) /* Stops current DMA operation by clearing DMAST (ECON1<5>) */
+#define ENC_DMACKSUM (0xd8) /* Starts DMA and checksum operation (sets ECON1<5:2> = 1000) */
+#define ENC_DMACKSUMS (0xda) /* Starts DMA checksum operation with seed (sets ECON1<5:2> = 1010) */
+#define ENC_DMACOPY (0xdc) /* Starts DMA copy and checksum operation (sets ECON1<5:2> = 1100) */
+#define ENC_DMACOPYS (0xde) /* Starts DMA copy and checksum operation with seed (sets ECON1<5:2> = 1110) */
+#define ENC_SETTXRTS (0xd4) /* Sets TXRTS (ECON1<1>), sends an Ethernet packet */
+#define ENC_ENABLERX (0xe8) /* Enables packet reception by setting RXEN (ECON1<0>) */
+#define ENC_DISABLERX (0xea) /* Disables packet reception by clearing RXEN (ECON1<0>) */
+#define ENC_SETEIE (0xec) /* Enable Ethernet Interrupts by setting INT (ESTAT<15>) */
+#define ENC_CLREIE (0xee) /* Disable Ethernet Interrupts by clearing INT (ESTAT<15>) */
+
+/* Two-Byte Instructions */
+
+/* There is only one instruction in the ENCX24J600 command set which uses two
+ * SPI bytes. The Read Bank Select opcode, RBSEL, reads the internal SFR bank
+ * select state and returns the value to the host controller.
+ */
+
+#define ENC_RBSEL (0xc8)
+
+/* Three-Byte Instructions */
+
+#define ENC_WGPRDPT (0x60) /* Write General Purpose Buffer Read Pointer (EGPRDPT) */
+#define ENC_RGPRDPT (0x62) /* Read General Purpose Buffer Read Pointer (EGPRDPT) */
+#define ENC_WRXRDPT (0x64) /* Write Receive Buffer Read Pointer (ERXRDPT) */
+#define ENC_RRXRDPT (0x66) /* Read Receive Buffer Read Pointer (ERXRDPT) */
+#define ENC_WUDARDPT (0x68) /* Write User-Defined Area Read Pointer (EUDARDPT) */
+#define ENC_RUDARDPT (0x6a) /* Read User-Defined Area Read Pointer (EUDARDPT) */
+#define ENC_WGPWRPT (0x6c) /* Write General Purpose Buffer Write Pointer (EGPWRPT) */
+#define ENC_RGPWRPT (0x6e) /* Read General Purpose Buffer Write Pointer (EGPWRPT) */
+#define ENC_WRXWRPT (0x70) /* Write Receive Buffer Write Pointer (ERXWRPT) */
+#define ENC_RRXWRPT (0x72) /* Read Receive Buffer Write Pointer (ERXWRPT) */
+#define ENC_WUDAWRPT (0x78) /* Write User-Defined Area Write Pointer (EUDAWRPT) */
+#define ENC_RUDAWRPT (0x76) /* Read User-Defined Area Write Pointer (EUDAWRPT) */
+
+/* Banked N-Byte Instructions */
+
+#define ENC_RCR (0x00) /* Read Control Register
+ 000 | aaaaa | (Register value returned)) */
+#define ENC_WCR (0x40) /* Write Control Register
+ 010 | aaaaa | dddddddd */
+#define ENC_BFS (0x80) /* Bit Field Set
+ 100 | aaaaa | dddddddd */
+#define ENC_BFC (0xa0) /* Bit Field Clear
+ 101 | aaaaa | dddddddd */
+
+/* Unbanked N-Byte Instructions */
+
+#define ENC_RCRU (0x20) /* Read Control Register(s), Unbanked */
+#define ENC_WCRU (0x22) /* Write Control Register(s), Unbanked */
+#define ENC_BFSU (0x24) /* Bit Field(s) Set, Unbanked */
+#define ENC_BFCU (0x26) /* Bit Field(s) Clear, Unbanked */
+
+/* SRAM Access Instructions */
+
+#define ENC_RGPDATA (0x28) /* Read Data from EGPDATA */
+#define ENC_WGPDATA (0x2a) /* Write Data from EGPDATA */
+#define ENC_RRXDATA (0x2c) /* Read Data from ERXDATA */
+#define ENC_WRXDATA (0x2e) /* Write Data from ERXDATA */
+#define ENC_RUDADATA (0x30) /* Read Data from EUDADATA */
+#define ENC_WUDADATA (0x32) /* Write Data from EUDADATA */
+
+/* Banked Control Registers *************************************************/
+/* Registers are described by 16 bit values. The high byte describes the bank
+ * by the appropiate bank selection command.
+ * For registers which are available on all banks the comnmand is set to 0.
+ * Unbanked registers are identified by 0x01.
+ */
+
+#define ENC_ADDR_SHIFT (0)
+#define ENC_ADDR_MASK (0xff << ENC_ADDR_SHIFT)
+#define ENC_BANK_SHIFT (8)
+#define ENC_BANK_MASK (0xff << ENC_BANK_SHIFT)
+
+#define REGADDR(a,b) ((b) << ENC_BANK_SHIFT | (a) << ENC_ADDR_SHIFT)
+#define GETADDR(a) (((a) & ENC_ADDR_MASK) >> ENC_ADDR_SHIFT)
+#define GETBANK(a) (((a) & ENC_BANK_MASK) >> ENC_BANK_SHIFT)
+
+/* Bank 0 Control Register Addresses */
+
+#define ENC_ETXST REGADDR(0x00, ENC_B0SEL)
+#define ENC_ETXLEN REGADDR(0x02, ENC_B0SEL)
+#define ENC_ERXST REGADDR(0x04, ENC_B0SEL)
+#define ENC_ERXTAIL REGADDR(0x06, ENC_B0SEL)
+#define ENC_ERXHEAD REGADDR(0x08, ENC_B0SEL)
+#define ENC_EDMAST REGADDR(0x0a, ENC_B0SEL)
+#define ENC_EDMALEN REGADDR(0x0c, ENC_B0SEL)
+#define ENC_EDMADST REGADDR(0x0e, ENC_B0SEL)
+#define ENC_EDMACS REGADDR(0x10, ENC_B0SEL)
+#define ENC_ETXSTAT REGADDR(0x12, ENC_B0SEL)
+#define ENC_ETXWIRE REGADDR(0x14, ENC_B0SEL)
+
+/* Bank 1 Contro Register Addresses */
+
+#define ENC_EHT1 REGADDR(0x00, ENC_B1SEL)
+#define ENC_EHT2 REGADDR(0x02, ENC_B1SEL)
+#define ENC_EHT3 REGADDR(0x04, ENC_B1SEL)
+#define ENC_EHT4 REGADDR(0x06, ENC_B1SEL)
+#define ENC_EPMM1 REGADDR(0x08, ENC_B1SEL)
+#define ENC_EPMM2 REGADDR(0x0a, ENC_B1SEL)
+#define ENC_EPMM3 REGADDR(0x0c, ENC_B1SEL)
+#define ENC_EPMM4 REGADDR(0x0e, ENC_B1SEL)
+#define ENC_EPMCS REGADDR(0x10, ENC_B1SEL)
+#define ENC_EPMO REGADDR(0x12, ENC_B1SEL)
+#define ENC_ERXFCON REGADDR(0x14, ENC_B1SEL)
+
+/* Bank 2 Control Register Addresses */
+
+#define ENC_MACON1 REGADDR(0x00, ENC_B2SEL)
+#define ENC_MACON2 REGADDR(0x02, ENC_B2SEL)
+#define ENC_MABBIPG REGADDR(0x04, ENC_B2SEL)
+#define ENC_MAIPG REGADDR(0x06, ENC_B2SEL)
+#define ENC_MACLCON REGADDR(0x08, ENC_B2SEL)
+#define ENC_MAMXFL REGADDR(0x0a, ENC_B2SEL)
+/* 0x0c - 0x11 reserved */
+#define ENC_MICMD REGADDR(0x12, ENC_B2SEL)
+#define ENC_MIREGADR REGADDR(0x14, ENC_B2SEL)
+
+/* MAC Control Register 1 Bit Definitions */
+
+#define MACON1_PASSALL (1 << 1)
+#define MACON1_RXPAUS (1 << 2)
+#define MACON1_LOOPBK (1 << 4)
+
+/* MAC Control Register 2 Bit Definitions */
+
+#define MACON2_FULDPX (1 << 0) /* MAC Full-Duplex Enable bit */
+#define MACON2_HFRMEN (1 << 2) /* Huge Frame Enable bit */
+#define MACON2_PHDREN (1 << 3) /* Proprietary Header Enable bit */
+#define MACON2_TXCRCEN (1 << 4) /* Transmit CRC Enable bit */
+#define MACON2_PADCFG0 (1 << 5) /* Automatic Pad and CRC Configuration bits */
+#define MACON2_PADCFG1 (1 << 6)
+#define MACON2_PADCFG2 (1 << 7)
+#define MACON2_NOBKOFF (1 << 12) /* No Backoff Enable bit (applies to half duplex only) */
+#define MACON2_BPEN (1 << 13) /* No Backoff During Back Pressure Enable bit (applies to half duplex only) */
+#define MACON2_DEFER (1 << 14) /* Defer Transmission Enable bit (applies to half duplex only) */
+
+/* MII Management Command Register Bit Definitions */
+
+#define MICMD_MIIRD (1 << 0) /* MII Read Enable bit */
+#define MICMD_MIISCAN (1 << 1) /* MII Scan Enable bit */
+
+/* MII Management Status Register Bit Definitions */
+
+#define MISTAT_BUSY (1 << 0) /* MII Management Busy Status bit */
+#define MISTAT_SCAN (1 << 1) /* MII Management Scan Status bit */
+#define MISTAT_NVALID (1 << 2) /* MII Management Read Data Not Valid Status bit */
+
+/* Bank 3 Control Register Addresses */
+
+#define ENC_MAADR3 REGADDR(0x00, ENC_B3SEL)
+#define ENC_MAADR2 REGADDR(0x02, ENC_B3SEL)
+#define ENC_MAADR1 REGADDR(0x04, ENC_B3SEL)
+#define ENC_MIWR REGADDR(0x06, ENC_B3SEL)
+#define ENC_MIRD REGADDR(0x08, ENC_B3SEL)
+#define ENC_MISTAT REGADDR(0x0a, ENC_B3SEL)
+#define ENC_EPAUS REGADDR(0x0c, ENC_B3SEL)
+#define ENC_ECON2 REGADDR(0x0e, ENC_B3SEL)
+#define ENC_ERXWM REGADDR(0x10, ENC_B3SEL)
+#define ENC_EIE REGADDR(0x12, ENC_B3SEL)
+#define ENC_EIDLED REGADDR(0x14, ENC_B3SEL)
+
+/* Ethernet Control Register Bit Definitions */
+
+#define ECON2_AESLEN0 (1 << 0) /* AES Key Length Control bits */
+#define ECON2_AESLEN1 (1 << 1) /* Modular Exponentiation Length Control bits */
+#define ECON2_MODLEN0 (1 << 2)
+#define ECON2_MODLEN1 (1 << 3)
+#define ECON2_ETHRST (1 << 4) /* Master Ethernet Reset bit */
+#define ECON2_RXRST (1 << 5) /* Receive Logic Reset bit */
+#define ECON2_TXRST (1 << 6) /* Transmit Logic Reset bit */
+#define ECON2_AUTOFC (1 << 7) /* Automatic Flow Control Enable bit */
+#define ECON2_COCON_SHIFT (8) /* CLKOUT Frequency Control bits */
+#define ECON2_COCON_MASK (0x0f << ECON2_COCON_SHIFT)
+#define ECON2_SHA1MD5 (1 << 12) /* SHA-1/MD5 Hash Control bit */
+#define ECON2_TXMAC (1 << 13) /* Automatically Transmit MAC Address Enable bit */
+#define ECON2_STRCH (1 << 14) /* LED Stretching Enable bit */
+#define ECON2_ETHEN (1 << 15) /* Ethernet Enable bit */
+
+/* Ethernet Interrupt Enable Register Bit Definitions */
+
+#define EIE_PCFULIE (1 << 0) /* Packet Counter Full Interrupt Enable bit */
+#define EIE_RXABTIE (1 << 1) /* Receive Abort Interrupt Enable bit */
+#define EIE_TXABTIE (1 << 2) /* Transmit Abort Interrupt Enable bit */
+#define EIE_TXIE (1 << 3) /* Transmit Done Interrupt Enable bit */
+#define EIE_DMAIE (1 << 5) /* DMA Interrupt Enable bit */
+#define EIE_PKTIE (1 << 6) /* RX Packet Pending Interrupt Enable bit */
+#define EIE_LINKIE (1 << 11) /* PHY Link Status Change Interrupt Enable bit */
+#define EIE_AESIE (1 << 12) /* AES Encrypt/Decrypt Interrupt Enable bit */
+#define EIE_HASHIE (1 << 13) /* MD5/SHA-1 Hash Interrupt Enable bit */
+#define EIE_MODEXIE (1 << 14) /* Modular Exponentiation Interrupt Enable bit */
+#define EIE_INTIE (1 << 15) /* INT Global Interrupt Enable bit */
+
+/**
+ * The last 10 bytes (16h to 1Fh) of all SPI banks point to a common set of five
+ * registers: EUDAST, EUDAND, ESTAT, EIR and ECON1. These are key registers used
+ * in controlling and monitoring the operation of the device. Their common
+ * banked addresses allow easy access without switching the bank.
+ */
+
+/* Common Register Addresses */
+
+#define ENC_EUDAST REGADDR(0x16, 0x00) /* User-Defined Area Start Pointer (EUDAST<7:0>) */
+#define ENC_EUDAND REGADDR(0x18, 0x00) /* User-Defined Area End Pointer (EUDAND<7:0>) */
+#define ENC_ESTAT REGADDR(0x1a, 0x00)
+#define ENC_EIR REGADDR(0x1c, 0x00)
+#define ENC_ECON1 REGADDR(0x1e, 0x00)
+
+/* Ethernet Status Register Bit Definitions */
+
+#define ESTAT_PKTCNT_SHIFT (0) /* Receive Packet Count bits */
+#define ESTAT_PKTCNT_MASK (0xff)
+#define ESTAT_PHYLNK (1 << 8) /* PHY Linked Status bit */
+#define ESTAT_PHYDPX (1 << 10) /* PHY Full Duplex Status bit */
+#define ESTAT_CLKRDY (1 << 12) /* Clock Ready Status bit */
+#define ESTAT_RXBUSY (1 << 13) /* Receive Logic Active Status bit */
+#define ESTAT_FCIDLE (1 << 14) /* Flow Control Idle Status bit */
+#define ESTAT_INT (1 << 15) /* Interrupt Pending Status bit */
+
+/* Ethernet Interrupt Flag Register Bit Definitions */
+
+#define EIR_PCFULIF (1 << 0) /* Packet Counter Full Interrupt Flag bit */
+#define EIR_RXABTIF (1 << 1) /* Receive Abort Interrupt Flag bit */
+#define EIR_TXABTIF (1 << 2) /* Transmit Abort Interrupt Flag bit */
+#define EIR_TXIF (1 << 3) /* Transmit Done Interrupt Flag bit */
+#define EIR_DMAIF (1 << 5) /* DMA Interrupt Flag bit */
+#define EIR_PKTIF (1 << 6) /* RX Packet Pending Interrupt Flag bit */
+#define EIR_LINKIF (1 << 11) /* PHY Link Status Change Interrupt Flag bit */
+#define EIR_AESIF (1 << 12) /* AES Encrypt/Decrypt Interrupt Flag bit */
+#define EIR_HASHIF (1 << 13) /* MD5/SHA-1 Hash Interrupt Flag bit */
+#define EIR_MODEXIF (1 << 14) /* Modular Exponentiation Interrupt Flag bit */
+#define EIR_CRYPTEN (1 << 15) /* Modular Exponentiation and AES Cryptographic Modules Enable bit */
+#define EIR_ALLINTS (0xf86f)
+
+/* Ethernet Control Register 1 Bit Definitions */
+
+#define ECON1_RXEN (1 << 0) /* Receive Enable bit */
+#define ECON1_TXRTS (1 << 1) /* Transmit Request to Send Status/Control bit */
+#define ECON1_DMANOCS (1 << 2) /* DMA No Checksum Control bit */
+#define ECON1_DMACSSD (1 << 3) /* DMA Checksum Seed Control bit */
+#define ECON1_DMACPY (1 << 4) /* DMA Copy Control bit */
+#define ECON1_DMAST (1 << 5) /* DMA Start bit */
+#define ECON1_FCOP0 (1 << 6) /* Flow Control Operation Control/Status bits */
+#define ECON1_FCOP1 (1 << 7) /* Flow Control Operation Control/Status bits */
+#define ECON1_PKTDEC (1 << 8) /* RX Packet Counter Decrement Control bit */
+#define ECON1_AESOP0 (1 << 9) /* AES Operation Control bits */
+#define ECON1_AESOP1 (1 << 10) /* AES Operation Control bits */
+#define ECON1_AESST (1 << 11) /* AES Encrypt/Decrypt Start bit */
+#define ECON1_HASHLST (1 << 12) /* MD5/SHA-1 Hash Last Block Control bit */
+#define ECON1_HASHOP (1 << 13) /* MD5/SHA-1 Hash Operation Control bit */
+#define ECON1_HASHEN (1 << 14) /* MD5/SHA-1 Hash Enable bit */
+#define ECON1_MODEXST (1 << 15) /* Modular Exponentiation Start bit */
+
+/* Unbanked Register Addresses */
+
+#if 0
+/* Disabled to prevent accidental use. All unbanked operations are implemented
+ * using the specific manipulation commands.
+ */
+#define ENC_EGPDATA 0x80
+#define ENC_ERXDATA 0x82
+#define ENC_EUDADATA 0x84
+#define ENC_EGPRDPT 0x86
+#define ENC_EGPWRPT 0x88
+#define ENC_ERXRDPT 0x8a
+#define ENC_ERXWRPT 0x8c
+#define ENC_EUDARDPT 0x8e
+#define ENC_EUDAWRPT 0x90
+#endif
+
+/* PHY Registers ************************************************************/
+
+#define ENC_PHCON1 0x00
+#define ENC_PHSTAT1 0x01
+#define ENC_PHANA 0x04
+#define ENC_PHANLPA 0x05
+#define ENC_PHANE 0x06
+#define ENC_PHCON2 0x11
+#define ENC_PHSTAT2 0x1b
+#define ENC_PHSTAT3 0x1f
+
+/* PHY Control Register 1 Bit Definitions */
+
+#define PHCON1_PFULDPX (1 << 8) /* PHY Duplex Select Control bit */
+#define PHCON1_RENEG (1 << 9) /* Restart Auto-Negotiation Control bit */
+#define PHCON1_PSLEEP (1 << 11) /* PHY Sleep Enable bit */
+#define PHCON1_ANEN (1 << 12) /* PHY Auto-Negotiation Enable bit */
+#define PHCON1_SPD100 (1 << 13) /* PHY Speed Select Control bit */
+#define PHCON1_PLOOPBK (1 << 14) /* PHY Loopback Enable bit */
+#define PHCON1_PRST (1 << 15) /* PHY Reset bit */
+
+/* PHY Status Register 1 Bit Definitions */
+
+#define PHSTAT1_EXTREGS (1 << 0) /* Extended Capabilities Registers Present Status bit */
+#define PHSTAT1_LLSTAT (1 << 2) /* Latching Link Status bit */
+#define PHSTAT1_ANABLE (1 << 3) /* Auto-Negotiation Ability Status bit */
+#define PHSTAT1_LRFAULT (1 << 4) /* Latching Remote Fault Condition Status bit */
+#define PHSTAT1_ANDONE (1 << 5) /* Auto-Negotiation Done Status bit */
+#define PHSTAT1_HALF10 (1 << 11) /* 10Base-T Half-Duplex Ability Status bit */
+#define PHSTAT1_FULL10 (1 << 12) /* 10Base-T Full-Duplex Ability Status bit */
+#define PHSTAT1_HALF100 (1 << 13) /* 100Base-TX Half-Duplex Ability Status bit */
+#define PHSTAT1_FULL100 (1 << 13) /* 100Base-TX Full-Duplex Ability Status bit */
+
+/* PHY Auto-Negotiation Advertisement Register Bit Definitions */
+
+
+#define PHANA_ADIEEE0 (1 << 0)
+#define PHANA_ADIEEE1 (1 << 1)
+#define PHANA_ADIEEE2 (1 << 2)
+#define PHANA_ADIEEE3 (1 << 3)
+#define PHANA_ADIEEE4 (1 << 4)
+#define PHANA_AD10 (1 << 5) /* Advertise 10Base-T Half-Duplex Ability bit */
+#define PHANA_AD10FD (1 << 6) /* Advertise 10Base-T Full-Duplex Ability bit */
+#define PHANA_AD100 (1 << 7) /* Advertise 100Base-TX Half-Duplex Ability bit */
+#define PHANA_AD100FD (1 << 8) /* Advertise 100Base-TX Full-Duplex Ability bit */
+/* Advertise PAUSE Flow Control Ability bits */
+/* 11 = Local device supports both symmetric PAUSE and asymmetric PAUSE toward local device */
+/* 10 = Local device supports asymmetric PAUSE toward link partner only */
+/* 01 = Local device supports symmetric PAUSE only (Normal Flow Control mode) */
+/* 00 = Local device does not support PAUSE flow control */
+#define PHANA_ADPAUS0 (1 << 10)
+#define PHANA_ADPAUS1 (1 << 11)
+#define PHANA_ADFAULT (1 << 13) /* Advertise Remote Fault Condition bit */
+#define PHANA_ADNP (1 << 15) /* Advertise Next Page Ability bit */
+
+/* Packet Memory ************************************************************/
+
+/* 24-Kbyte Transmit/Receive Packet Dual Port SRAM */
+
+#define PKTMEM_START 0x0000
+#define PKTMEM_END 0x5fff
+
+/* RX Status Bit Definitions ************************************************/
+
+#define RXSTAT_OK (1 << 7)
+
+#endif /* __DRIVERS_NET_ENCX24J600_H */
diff --git a/nuttx/include/nuttx/net/encx24j600.h b/nuttx/include/nuttx/net/encx24j600.h
new file mode 100644
index 000000000..d32358383
--- /dev/null
+++ b/nuttx/include/nuttx/net/encx24j600.h
@@ -0,0 +1,181 @@
+/****************************************************************************
+ * include/nuttx/net/encx24j600.h
+ *
+ * Copyright (C) 2013 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_NET_ENCX24J600_H
+#define __INCLUDE_NUTTX_NET_ENCX24J600_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <nuttx/irq.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* ENCX24J600 Configuration Settings:
+ *
+ * CONFIG_ENCX24J600 - Enabled ENCX24J600 support
+ * CONFIG_ENCX24J600_SPIMODE - Controls the SPI mode
+ * CONFIG_ENCX24J600_FREQUENCY - Define to use a different bus frequency
+ * CONFIG_ENCX24J600_NINTERFACES - Specifies the number of physical ENCX24J600
+ * devices that will be supported.
+ * CONFIG_ENCX24J600_STATS - Collect network statistics
+ * CONFIG_ENCX24J600_HALFDUPPLEX - Default is full duplex
+ */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* This structure returns driver statistics (if enabled) */
+
+#ifdef CONFIG_ENCX24J600_STATS
+struct enc_stats_s
+{
+ uint8_t maxpktcnt; /* Max. number of buffered RX packets */
+ uint32_t txrequests; /* Number of TX packets queued */
+ uint32_t txifs; /* TXIF completion events */
+ uint32_t txabrts; /* TXIF completions with ESTAT.TXABRT */
+ uint32_t txerifs; /* TXERIF error events */
+ uint32_t txtimeouts; /* S/W detected TX timeouts */
+ uint32_t pktifs; /* PKTIF RX completion events */
+ uint32_t rxnotok; /* PKTIF without RXSTAT_OK */
+ uint32_t rxpktlen; /* PKTIF with bad pktlen */
+ uint32_t rxerifs; /* RXERIF error evernts */
+};
+#endif
+
+/* The ENCX24J600 normal provides interrupts to the MCU via a GPIO pin. The
+ * following structure provides an MCU-independent mechanixm for controlling
+ * the ENCX24J600 GPIO interrupt.
+ *
+ * The ENC32J60 interrupt is an active low, *level* interrupt. "When an
+ * interrupt occurs, the interrupt flag is set. If the interrupt is enabled
+ * in the EIE register and the INTIE global interrupt enable bit is set, the
+ * INT pin will be driven low"
+ *
+ * "When an enabled interrupt occurs, the interrupt pin will remain low until
+ * all flags which are causing the interrupt are cleared or masked off
+ * (enable bit is cleared) by the host controller." However, the interrupt
+ * will behave like a falling edge interrupt because "After an interrupt
+ * occurs, the host controller [clears] the global enable bit for the
+ * interrupt pin before servicing the interrupt. Clearing the enable bit
+ * will cause the interrupt pin to return to the non-asserted state (high).
+ * Doing so will prevent the host controller from missing a falling edge
+ * should another interrupt occur while the immediate interrupt is being
+ * serviced."
+ */
+
+struct enc_lower_s
+{
+ int (*attach)(FAR const struct enc_lower_s *lower, xcpt_t handler);
+ void (*enable)(FAR const struct enc_lower_s *lower);
+ void (*disable)(FAR const struct enc_lower_s *lower);
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: enc_initialize
+ *
+ * Description:
+ * Initialize the Ethernet driver. The ENCX24J600 device is assumed to be
+ * in the post-reset state upon entry to this function.
+ *
+ * Parameters:
+ * spi - A reference to the platform's SPI driver for the ENCX24J600
+ * lower - The MCU-specific interrupt used to control low-level MCU
+ * functions (i.e., ENCX24J600 GPIO interrupts).
+ * devno - If more than one ENCX24J600 is supported, then this is the
+ * zero based number that identifies the ENCX24J600;
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+struct spi_dev_s; /* see nuttx/spi/spi.h */
+int enc_initialize(FAR struct spi_dev_s *spi,
+ FAR const struct enc_lower_s *lower, unsigned int devno);
+
+/****************************************************************************
+ * Function: enc_stats
+ *
+ * Description:
+ * Return accumulated ENCX24J600 statistics. Statistics are cleared after
+ * being returned.
+ *
+ * Parameters:
+ * devno - If more than one ENCX24J600 is supported, then this is the
+ * zero based number that identifies the ENCX24J600;
+ * stats - The user-provided location to return the statistics.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ENCX24J600_STATS
+int enc_stats(unsigned int devno, struct enc_stats_s *stats);
+#endif
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_NUTTX_NET_ENCX24J600_H */