You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2020/12/23 19:56:38 UTC
[incubator-nuttx] 03/03: arch: imx6: Add imx_enet driver
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit ace6e70f5774ebf04f798b9d209eb947cd0eb3b3
Author: Masayuki Ishikawa <ma...@gmail.com>
AuthorDate: Wed Dec 23 19:27:49 2020 +0900
arch: imx6: Add imx_enet driver
Summary:
- This commit adds imx_enet driver derived from imxrt_enet
Impact:
- imx6 only
Testing:
- Tested with sabre-6quad:netnsh
- NOTE: telnetd works with QEMU
---
arch/arm/src/imx6/Kconfig | 40 +
arch/arm/src/imx6/Make.defs | 4 +
arch/arm/src/imx6/hardware/imx_enet.h | 701 ++++++
arch/arm/src/imx6/imx_enet.c | 2549 ++++++++++++++++++++
arch/arm/src/imx6/imx_enet.h | 108 +
.../arm/imx6/sabre-6quad/configs/netnsh/defconfig | 88 +
6 files changed, 3490 insertions(+)
diff --git a/arch/arm/src/imx6/Kconfig b/arch/arm/src/imx6/Kconfig
index e9ad1e5..b71c49d 100644
--- a/arch/arm/src/imx6/Kconfig
+++ b/arch/arm/src/imx6/Kconfig
@@ -120,8 +120,47 @@ config IMX6_SPI2
default n
select SPI
+config IMX6_ENET
+ bool "Ethernet"
+ default n
+ select ARCH_HAVE_PHY
+ select ARCH_PHY_INTERRUPT
+ select ARCH_HAVE_NETDEV_STATISTICS
+
endmenu # iMX Peripheral Selection
+menu "Ethernet Configuration"
+ depends on IMX6_ENET
+
+config IMX_ENET_NRXBUFFERS
+ int "Number Rx buffers"
+ default 6
+
+config IMX_ENET_NTXBUFFERS
+ int "Number Tx buffers"
+ default 2
+
+config IMX_ENET_ENHANCEDBD
+ bool # not optional
+ default n
+
+config IMX_ENET_NETHIFS
+ int # Not optional
+ default 1
+
+config IMX_ENET_PHYINIT
+ bool "Board-specific PHY Initialization"
+ default n
+ ---help---
+ Some boards require specialized initialization of the PHY before it
+ can be used. This may include such things as configuring GPIOs,
+ resetting the PHY, etc. If CONFIG_IMX_ENET_PHYINIT is defined in
+ the configuration then the board specific logic must provide
+ imx_phy_boardinitialize(); The i.MX6 ENET driver will call this
+ function one time before it first uses the PHY.
+
+endmenu # IMX_ENET
+
config IMX_DDR_SIZE
int "Installed DRAM size (bytes)"
default 268435456
@@ -152,4 +191,5 @@ config IMX6_BOOT_SRAM
select BOOT_RUNFROMEXTSRAM
endchoice # i.MX6 Boot Configuration
+
endif # ARCH_CHIP_IMX6
diff --git a/arch/arm/src/imx6/Make.defs b/arch/arm/src/imx6/Make.defs
index eb84c44..bedf7fb 100644
--- a/arch/arm/src/imx6/Make.defs
+++ b/arch/arm/src/imx6/Make.defs
@@ -143,3 +143,7 @@ endif
ifeq ($(CONFIG_IMX6_ECSPI),y)
CHIP_CSRCS += imx_ecspi.c
endif
+
+ifeq ($(CONFIG_IMX6_ENET),y)
+CHIP_CSRCS += imx_enet.c
+endif
diff --git a/arch/arm/src/imx6/hardware/imx_enet.h b/arch/arm/src/imx6/hardware/imx_enet.h
new file mode 100644
index 0000000..34a649c
--- /dev/null
+++ b/arch/arm/src/imx6/hardware/imx_enet.h
@@ -0,0 +1,701 @@
+/************************************************************************************
+ * arch/arm/src/imx6/hardware/imx_enet.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ************************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_IMX6_HARDWARE_IMX_ENET_H
+#define __ARCH_ARM_SRC_IMX6_HARDWARE_IMX_ENET_H
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+
+/************************************************************************************
+ * Pre-processor Definitions
+ ************************************************************************************/
+
+#define IMX_ENET_HAS_DBSWAP 1
+
+/* Register Offsets *****************************************************************/
+
+#define IMX_ENET_EIR_OFFSET 0x0004 /* Interrupt Event Register */
+#define IMX_ENET_EIMR_OFFSET 0x0008 /* Interrupt Mask Register */
+#define IMX_ENET_RDAR_OFFSET 0x0010 /* Receive Descriptor Active Register */
+#define IMX_ENET_TDAR_OFFSET 0x0014 /* Transmit Descriptor Active Register */
+#define IMX_ENET_ECR_OFFSET 0x0024 /* Ethernet Control Register */
+#define IMX_ENET_MMFR_OFFSET 0x0040 /* MII Management Frame Register */
+#define IMX_ENET_MSCR_OFFSET 0x0044 /* MII Speed Control Register */
+#define IMX_ENET_MIBC_OFFSET 0x0064 /* MIB Control Register */
+#define IMX_ENET_RCR_OFFSET 0x0084 /* Receive Control Register */
+#define IMX_ENET_TCR_OFFSET 0x00c4 /* Transmit Control Register */
+#define IMX_ENET_PALR_OFFSET 0x00e4 /* Physical Address Lower Register */
+#define IMX_ENET_PAUR_OFFSET 0x00e8 /* Physical Address Upper Register */
+#define IMX_ENET_OPD_OFFSET 0x00ec /* Opcode/Pause Duration Register */
+
+#if 0
+#define IMX_ENET_TXIC_OFFSET 0x00f0 /* Transmit Interrupt Coalescing Register */
+#define IMX_ENET_RXIC_OFFSET 0x0100 /* Receive Interrupt Coalescing Register */
+#endif
+
+#define IMX_ENET_IAUR_OFFSET 0x0118 /* Descriptor Individual Upper Address Register */
+#define IMX_ENET_IALR_OFFSET 0x011c /* Descriptor Individual Lower Address Register */
+#define IMX_ENET_GAUR_OFFSET 0x0120 /* Descriptor Group Upper Address Register */
+#define IMX_ENET_GALR_OFFSET 0x0124 /* Descriptor Group Lower Address Register */
+#define IMX_ENET_TFWR_OFFSET 0x0144 /* Transmit FIFO Watermark Register */
+#define IMX_ENET_RDSR_OFFSET 0x0180 /* Receive Descriptor Ring Start Register */
+#define IMX_ENET_TDSR_OFFSET 0x0184 /* Transmit Buffer Descriptor Ring Start Register */
+#define IMX_ENET_MRBR_OFFSET 0x0188 /* Maximum Receive Buffer Size Register */
+#define IMX_ENET_RSFL_OFFSET 0x0190 /* Receive FIFO Section Full Threshold */
+#define IMX_ENET_RSEM_OFFSET 0x0194 /* Receive FIFO Section Empty Threshold */
+#define IMX_ENET_RAEM_OFFSET 0x0198 /* Receive FIFO Almost Empty Threshold */
+#define IMX_ENET_RAFL_OFFSET 0x019c /* Receive FIFO Almost Full Threshold */
+#define IMX_ENET_TSEM_OFFSET 0x01a0 /* Transmit FIFO Section Empty Threshold */
+#define IMX_ENET_TAEM_OFFSET 0x01a4 /* Transmit FIFO Almost Empty Threshold */
+#define IMX_ENET_TAFL_OFFSET 0x01a8 /* Transmit FIFO Almost Full Threshold */
+#define IMX_ENET_TIPG_OFFSET 0x01ac /* Transmit Inter-Packet Gap */
+#define IMX_ENET_FTRL_OFFSET 0x01b0 /* Frame Truncation Length */
+#define IMX_ENET_TACC_OFFSET 0x01c0 /* Transmit Accelerator Function Configuration */
+#define IMX_ENET_RACC_OFFSET 0x01c4 /* Receive Accelerator Function Configuration */
+
+/* TODO: statistic registers: 0x02xx */
+
+#define IMX_ENET_ATCR_OFFSET 0x0400 /* Timer Control Register */
+#define IMX_ENET_ATVR_OFFSET 0x0404 /* Timer Value Register */
+#define IMX_ENET_ATOFF_OFFSET 0x0408 /* Timer Offset Register */
+#define IMX_ENET_ATPER_OFFSET 0x040c /* Timer Period Register */
+#define IMX_ENET_ATCOR_OFFSET 0x0410 /* Timer Correction Register */
+#define IMX_ENET_ATINC_OFFSET 0x0414 /* Time-Stamping Clock Period Register */
+#define IMX_ENET_ATSTMP_OFFSET 0x0418 /* Timestamp of Last Transmitted Frame */
+
+#define IMX_ENET_TGSR_OFFSET 0x0604 /* Timer Global Status Register */
+#define IMX_ENET_TCSR0_OFFSET 0x0608 /* Timer Control Status Register */
+#define IMX_ENET_TCCR0_OFFSET 0x060c /* Timer Compare Capture Register */
+#define IMX_ENET_TCSR1_OFFSET 0x0610 /* Timer Control Status Register */
+#define IMX_ENET_TCCR1_OFFSET 0x0614 /* Timer Compare Capture Register */
+#define IMX_ENET_TCSR2_OFFSET 0x0618 /* Timer Control Status Register */
+#define IMX_ENET_TCCR2_OFFSET 0x061c /* Timer Compare Capture Register */
+#define IMX_ENET_TCSR3_OFFSET 0x0620 /* Timer Control Status Register */
+#define IMX_ENET_TCCR3_OFFSET 0x0624 /* Timer Compare Capture Register */
+
+/* Register Addresses ***************************************************************/
+
+#define IMX_ENET_EIR (IMX_ENET_VBASE+IMX_ENET_EIR_OFFSET)
+#define IMX_ENET_EIMR (IMX_ENET_VBASE+IMX_ENET_EIMR_OFFSET)
+#define IMX_ENET_RDAR (IMX_ENET_VBASE+IMX_ENET_RDAR_OFFSET)
+#define IMX_ENET_TDAR (IMX_ENET_VBASE+IMX_ENET_TDAR_OFFSET)
+#define IMX_ENET_ECR (IMX_ENET_VBASE+IMX_ENET_ECR_OFFSET)
+#define IMX_ENET_MMFR (IMX_ENET_VBASE+IMX_ENET_MMFR_OFFSET)
+#define IMX_ENET_MSCR (IMX_ENET_VBASE+IMX_ENET_MSCR_OFFSET)
+#define IMX_ENET_MIBC (IMX_ENET_VBASE+IMX_ENET_MIBC_OFFSET)
+#define IMX_ENET_RCR (IMX_ENET_VBASE+IMX_ENET_RCR_OFFSET)
+#define IMX_ENET_TCR (IMX_ENET_VBASE+IMX_ENET_TCR_OFFSET)
+#define IMX_ENET_PALR (IMX_ENET_VBASE+IMX_ENET_PALR_OFFSET)
+#define IMX_ENET_PAUR (IMX_ENET_VBASE+IMX_ENET_PAUR_OFFSET)
+#define IMX_ENET_OPD (IMX_ENET_VBASE+IMX_ENET_OPD_OFFSET)
+#define IMX_ENET_IAUR (IMX_ENET_VBASE+IMX_ENET_IAUR_OFFSET)
+#define IMX_ENET_IALR (IMX_ENET_VBASE+IMX_ENET_IALR_OFFSET)
+#define IMX_ENET_GAUR (IMX_ENET_VBASE+IMX_ENET_GAUR_OFFSET)
+#define IMX_ENET_GALR (IMX_ENET_VBASE+IMX_ENET_GALR_OFFSET)
+#define IMX_ENET_TFWR (IMX_ENET_VBASE+IMX_ENET_TFWR_OFFSET)
+#define IMX_ENET_RDSR (IMX_ENET_VBASE+IMX_ENET_RDSR_OFFSET)
+#define IMX_ENET_TDSR (IMX_ENET_VBASE+IMX_ENET_TDSR_OFFSET)
+#define IMX_ENET_MRBR (IMX_ENET_VBASE+IMX_ENET_MRBR_OFFSET)
+#define IMX_ENET_RSFL (IMX_ENET_VBASE+IMX_ENET_RSFL_OFFSET)
+#define IMX_ENET_RSEM (IMX_ENET_VBASE+IMX_ENET_RSEM_OFFSET)
+#define IMX_ENET_RAEM (IMX_ENET_VBASE+IMX_ENET_RAEM_OFFSET)
+#define IMX_ENET_RAFL (IMX_ENET_VBASE+IMX_ENET_RAFL_OFFSET)
+#define IMX_ENET_TSEM (IMX_ENET_VBASE+IMX_ENET_TSEM_OFFSET)
+#define IMX_ENET_TAEM (IMX_ENET_VBASE+IMX_ENET_TAEM_OFFSET)
+#define IMX_ENET_TAFL (IMX_ENET_VBASE+IMX_ENET_TAFL_OFFSET)
+#define IMX_ENET_TIPG (IMX_ENET_VBASE+IMX_ENET_TIPG_OFFSET)
+#define IMX_ENET_FTRL (IMX_ENET_VBASE+IMX_ENET_FTRL_OFFSET)
+#define IMX_ENET_TACC (IMX_ENET_VBASE+IMX_ENET_TACC_OFFSET)
+#define IMX_ENET_RACC (IMX_ENET_VBASE+IMX_ENET_RACC_OFFSET)
+
+#define IMX_ENET_ATCR (IMX_ENET_VBASE+IMX_ENET_ATCR_OFFSET)
+#define IMX_ENET_ATVR (IMX_ENET_VBASE+IMX_ENET_ATVR_OFFSET)
+#define IMX_ENET_ATOFF (IMX_ENET_VBASE+IMX_ENET_ATOFF_OFFSET)
+#define IMX_ENET_ATPER (IMX_ENET_VBASE+IMX_ENET_ATPER_OFFSET)
+#define IMX_ENET_ATCOR (IMX_ENET_VBASE+IMX_ENET_ATCOR_OFFSET)
+#define IMX_ENET_ATINC (IMX_ENET_VBASE+IMX_ENET_ATINC_OFFSET)
+#define IMX_ENET_ATSTMP (IMX_ENET_VBASE+IMX_ENET_ATSTMP_OFFSET)
+
+#define IMX_ENET_TGSR (IMX_ENET_VBASE+IMX_ENET_TGSR_OFFSET)
+#define IMX_ENET_TCSR0 (IMX_ENET_VBASE+IMX_ENET_TCSR0_OFFSET)
+#define IMX_ENET_TCCR0 (IMX_ENET_VBASE+IMX_ENET_TCCR0_OFFSET)
+#define IMX_ENET_TCSR1 (IMX_ENET_VBASE+IMX_ENET_TCSR1_OFFSET)
+#define IMX_ENET_TCCR1 (IMX_ENET_VBASE+IMX_ENET_TCCR1_OFFSET)
+#define IMX_ENET_TCSR2 (IMX_ENET_VBASE+IMX_ENET_TCSR2_OFFSET)
+#define IMX_ENET_TCCR2 (IMX_ENET_VBASE+IMX_ENET_TCCR2_OFFSET)
+#define IMX_ENET_TCSR3 (IMX_ENET_VBASE+IMX_ENET_TCSR3_OFFSET)
+#define IMX_ENET_TCCR3 (IMX_ENET_VBASE+IMX_ENET_TCCR3_OFFSET)
+
+/* Register Bit Definitions *********************************************************/
+
+/* Interrupt Event Register, Interrupt Mask Register */
+
+/* Bits 0-14: Reserved */
+#define ENET_INT_TS_TIMER (1 << 15) /* Bit 15: Timestamp timer */
+#define ENET_INT_TS_AVAIL (1 << 16) /* Bit 16: Transmit timestamp available */
+#define ENET_INT_WAKEUP (1 << 17) /* Bit 17: Node wake-up request indication */
+#define ENET_INT_PLR (1 << 18) /* Bit 18: Payload receive error */
+#define ENET_INT_UN (1 << 19) /* Bit 19: Transmit FIFO underrun */
+#define ENET_INT_RL (1 << 20) /* Bit 20: Collision Retry Limit */
+#define ENET_INT_LC (1 << 21) /* Bit 21: Late Collision */
+#define ENET_INT_EBERR (1 << 22) /* Bit 22: Ethernet Bus Error */
+#define ENET_INT_MII (1 << 23) /* Bit 23: MII Interrupt */
+#define ENET_INT_RXB (1 << 24) /* Bit 24: Receive Buffer Interrupt */
+#define ENET_INT_RXF (1 << 25) /* Bit 25: Receive Frame Interrupt */
+#define ENET_INT_TXB (1 << 26) /* Bit 26: Transmit Buffer Interrupt */
+#define ENET_INT_TXF (1 << 27) /* Bit 27: Transmit Frame Interrupt */
+#define ENET_INT_GRA (1 << 28) /* Bit 28: Graceful Stop Complete */
+#define ENET_INT_BABT (1 << 29) /* Bit 29: Babbling Transmit Error */
+#define ENET_INT_BABR (1 << 30) /* Bit 30: Babbling Receive Error */
+ /* Bit 31: Reserved */
+
+/* Receive Descriptor Active Register */
+
+/* Bits 0-23: Reserved */
+#define ENET_RDAR (1 << 24) /* Bit 24: Receive descriptor active */
+ /* Bits 25-31: Reserved */
+
+/* Transmit Descriptor Active Register */
+
+/* Bits 0-23: Reserved */
+#define ENET_TDAR (1 << 24) /* Bit 24: Transmit descriptor active */
+ /* Bits 25-31: Reserved */
+
+/* Ethernet Control Register */
+
+#define ENET_ECR_RESET (1 << 0) /* Bit 0: Ethernet MAC reset */
+#define ENET_ECR_ETHEREN (1 << 1) /* Bit 1: Ethernet enable */
+#define ENET_ECR_MAGICEN (1 << 2) /* Bit 2: Magic packet detection enable */
+#define ENET_ECR_SLEEP (1 << 3) /* Bit 3: Sleep mode enable */
+#define ENET_ECR_EN1588 (1 << 4) /* Bit 4: EN1588 enable */
+ /* Bit 5: Reserved */
+#define ENET_ECR_DBGEN (1 << 6) /* Bit 6: Debug enable */
+#define ENET_ECR_STOPEN (1 << 7) /* Bit 7: STOPEN Signal Control */
+#ifdef IMX_ENET_HAS_DBSWAP
+#define ENET_ECR_DBSWP (1 << 8) /* Bit 8: Swap bytes */
+#endif
+ /* Bits 9-31: Reserved */
+#define ECR_RESV_VAL (7 << 28) /* Reserve val to write */
+
+/* MII Management Frame Register */
+
+#define ENET_MMFR_DATA_SHIFT (0) /* Bits 0-15: Management frame data */
+#define ENET_MMFR_DATA_MASK (0xffff << ENET_MMFR_DATA_SHIFT)
+#define ENET_MMFR_TA_SHIFT (16) /* Bits 16-17: Turn around */
+#define ENET_MMFR_TA_MASK (3 << ENET_MMFR_TA_SHIFT)
+#define ENET_MMFR_RA_SHIFT (18) /* Bits 18-22: Register address */
+#define ENET_MMFR_RA_MASK (31 << ENET_MMFR_RA_SHIFT)
+#define ENET_MMFR_PA_SHIFT (23) /* Bits 23-27: PHY address */
+#define ENET_MMFR_PA_MASK (31 << ENET_MMFR_PA_SHIFT)
+#define ENET_MMFR_OP_SHIFT (28) /* Bits 28-29: Operation code */
+#define ENET_MMFR_OP_MASK (3 << ENET_MMFR_OP_SHIFT)
+#define ENET_MMFR_OP_WRNOTMII (0 << ENET_MMFR_OP_SHIFT) /* Write frame, not MII compliant */
+#define ENET_MMFR_OP_WRMII (1 << ENET_MMFR_OP_SHIFT) /* Write frame, MII management frame */
+#define ENET_MMFR_OP_RDMII (2 << ENET_MMFR_OP_SHIFT) /* Read frame, MII management frame */
+#define ENET_MMFR_OP_RdNOTMII (3 << ENET_MMFR_OP_SHIFT) /* Read frame, not MII compliant */
+#define ENET_MMFR_ST_SHIFT (30) /* Bits 30-31: Start of frame delimiter */
+#define ENET_MMFR_ST_MASK (3 << ENET_MMFR_ST_SHIFT)
+
+/* MII Speed Control Register */
+
+/* Bit 0: Reserved */
+#define ENET_MSCR_MII_SPEED_SHIFT (1) /* Bits 1-6: MII speed */
+#define ENET_MSCR_MII_SPEED_MASK (63 << ENET_MSCR_MII_SPEED_SHIFT)
+#define ENET_MSCR_DIS_PRE (1 << 7) /* Bit 7: Disable preamble */
+#define ENET_MSCR_HOLDTIME_SHIFT (8) /* Bits 8-10: Holdtime on MDIO output */
+#define ENET_MSCR_HOLDTIME_MASK (7 << ENET_MSCR_HOLDTIME_SHIFT)
+#define ENET_MSCR_HOLDTIME_1CYCLE (0 << ENET_MSCR_HOLDTIME_SHIFT) /* 1 internal module clock cycle */
+#define ENET_MSCR_HOLDTIME_2CYCLES (1 << ENET_MSCR_HOLDTIME_SHIFT) /* 2 internal module clock cycles */
+#define ENET_MSCR_HOLDTIME_3CYCLES (2 << ENET_MSCR_HOLDTIME_SHIFT) /* 3 internal module clock cycles */
+#define ENET_MSCR_HOLDTIME_8CYCLES (7 << ENET_MSCR_HOLDTIME_SHIFT) /* 8 internal module clock cycles */
+ /* Bits 11-31: Reserved */
+
+/* MIB Control Register */
+
+/* Bits 0-28: Reserved */
+#define ENET_MIBC_MIB_CLEAR (1 << 29) /* Bit 29: MIB clear */
+#define ENET_MIBC_MIB_IDLE (1 << 30) /* Bit 30: MIB idle */
+#define ENET_MIBC_MIB_DIS (1 << 31) /* Bit 31: Disable MIB logic */
+
+/* Receive Control Register */
+
+#define ENET_RCR_LOOP (1 << 0) /* Bit 0: Internal loopback */
+#define ENET_RCR_DRT (1 << 1) /* Bit 1: Disable receive on transmit */
+#define ENET_RCR_MII_MODE (1 << 2) /* Bit 2: Media independent interface mode */
+#define ENET_RCR_PROM (1 << 3) /* Bit 3: Promiscuous mode */
+#define ENET_RCR_BC_REJ (1 << 4) /* Bit 4: Broadcast frame reject */
+#define ENET_RCR_FCE (1 << 5) /* Bit 5: Flow control enable */
+ /* Bits 6-7: Reserved */
+#define ENET_RCR_RMII_MODE (1 << 8) /* Bit 8: RMII mode enable */
+#define ENET_RCR_RMII_10T (1 << 9) /* Bit 9: Enables 10-Mbps mode of the RMII */
+ /* Bits 10-11: Reserved */
+#define ENET_RCR_PADEN (1 << 12) /* Bit 12: Enable frame padding remove on receive */
+#define ENET_RCR_PAUFWD (1 << 13) /* Bit 13: Terminate/forward pause frames */
+#define ENET_RCR_CRCFWD (1 << 14) /* Bit 14: Terminate/forward received CRC */
+#define ENET_RCR_CFEN (1 << 15) /* Bit 15: MAC control frame enable */
+#define ENET_RCR_MAX_FL_SHIFT (16) /* Bits 16-29: Maximum frame length */
+#define ENET_RCR_MAX_FL_MASK (0x3fff << ENET_RCR_MAX_FL_SHIFT)
+#define ENET_RCR_NLC (1 << 30) /* Bit 30: Payload length check disable */
+#define ENET_RCR_GRS (1 << 31) /* Bit 31: Graceful receive stopped */
+
+/* Transmit Control Register */
+
+#define ENET_TCR_GTS (1 << 0) /* Bit 0: Graceful transmit stop */
+ /* Bit 1: Reserved */
+#define ENET_TCR_ADDINS (1 << 8) /* Bit 8: Set MAC address on transmit */
+#define ENET_TCR_FDEN (1 << 2) /* Bit 2: Full duplex enable */
+#define ENET_TCR_TFC_PAUSE (1 << 3) /* Bit 3: Transmit frame control pause */
+#define ENET_TCR_RFC_PAUSE (1 << 4) /* Bit 4: Receive frame control pause */
+#define ENET_TCR_ADDSEL_SHIFT (5) /* Bits 5-7: Source MAC address select on transmit */
+#define ENET_TCR_ADDSEL_MASK (7 << ENET_TCR_ADDSEL_SHIFT)
+#define ENET_TCR_ADDSEL_PADDR12 (0 << ENET_TCR_ADDSEL_SHIFT) /* Node MAC address programmed on PADDR1/2 registers */
+#define ENET_TCR_CRCFWD (1 << 9) /* Bit 9: Forward frame from application with CRC */
+ /* Bits 10-31: Reserved */
+
+/* Physical Address Lower/Upper Register (32-bits of 48-address) */
+
+/* Physical Address Upper Register */
+
+#define ENET_PAUR_TYPE_SHIFT (0) /* Bits 0-15: Type field in PAUSE frame */
+#define ENET_PAUR_TYPE_MASK (0xffff << ENET_PAUR_TYPE_MASK)
+#define ENET_PAUR_PADDR2_SHIFT (16) /* Bits 16-31: Bytes 4 and 5 of the 6-byte address */
+#define ENET_PAUR_PADDR2_MASK (0xffff << ENET_PAUR_PADDR2_SHIFT)
+
+/* Opcode/Pause Duration Register */
+
+#define ENET_OPD_PAUSE_DUR_SHIFT (0) /* Bits 0-15: Pause duration */
+#define ENET_OPD_PAUSE_DUR_MASK (0xffff << ENET_OPD_PAUSE_DUR_SHIFT)
+#define ENET_OPD_OPCODE_SHIFT (16) /* Bits 16-31: Opcode field in PAUSE frames */
+#define ENET_OPD_OPCODE_MASK (0xffff << ENET_OPD_OPCODE_SHIFT)
+
+/* Descriptor Individual Upper/Lower Address Register
+ * (64-bit address in two 32-bit registers)
+ * Descriptor Group Upper/Lower Address Register
+ * (64-bit address in two 32-bit registers)
+ */
+
+#if 0
+/* Transmit Interrupt Coalescing Register */
+
+#define ENET_TXIC_ICTT_SHIFT (0) /* Bits 0-15: Interrupt coalescing timer threshold */
+#define ENET_TXIC_ICTT_SHIFT_MASK (0xffff << ENET_TXIC_ICTT_SHIFT)
+ /* Bits 16-19: Reserved */
+#define ENET_TXIC_ICFT_SHIFT (20) /* Bits 0-15: Interrupt coalescing timer threshold */
+#define ENET_TXIC_ICFT_SHIFT_MASK (0xff << ENET_TXIC_ICFT_SHIFT)
+#define ENET_TXIC_ICTT_ICCS (1 << 30) /* Bit 30: Interrupt Coalescing Timer Clock Source Select */
+#define ENET_TXIC_ICTT_ICEN (1 << 31) /* Bit 31: Eable/disabel Interrupt Coalescing */
+
+/* Receive Interrupt Coalescing Register */
+
+#define ENET_RXIC_ICTT_SHIFT (0) /* Bits 0-15: Interrupt coalescing timer threshold */
+#define ENET_RXIC_ICTT_SHIFT_MASK (0xffff << ENET_TXIC_ICTT_SHIFT)
+ /* Bits 16-19: Reserved */
+#define ENET_RXIC_ICFT_SHIFT (20) /* Bits 0-15: Interrupt coalescing timer threshold */
+#define ENET_RXIC_ICFT_SHIFT_MASK (0xff << ENET_TXIC_ICFT_SHIFT)
+#define ENET_RXIC_ICTT_ICCS (1 << 30) /* Bit 30: Interrupt Coalescing Timer Clock Source Select */
+#define ENET_RXIC_ICTT_ICEN (1 << 31) /* Bit 31: Eable/disabel Interrupt Coalescing */
+
+#endif /* if 0 */
+
+/* Transmit FIFO Watermark Register */
+
+#define ENET_TFWR_TFWR_SHIFT (0) /* Bits 0-5: Transmit FIFO write */
+ /* Bits 6-7: Reserved */
+#define ENET_TFWR_TFWR_MASK (63 << ENET_TFWR_TFWR_SHIFT)
+#define ENET_TFWR_STRFWD (1 << 8) /* Bit 8: Store and forward enable */
+ /* Bits 9-31: Reserved */
+
+/* Receive Descriptor Ring Start Register */
+
+/* Bits 0-2: Reserved */
+#define ENET_RDSR_SHIFT (3) /* Bits 3-31: Start of the receive buffer descriptor queue */
+#define ENET_RDSR_MASK (0xfffffff8)
+
+/* Transmit Buffer Descriptor Ring Start Register */
+
+/* Bits 0-2: Reserved */
+#define ENET_TDSR_SHIFT (3) /* Bits 3-31: Start of the transmit buffer descriptor queue */
+#define ENET_TDSR_MASK (0xfffffff8)
+
+/* Maximum Receive Buffer Size Register */
+
+/* Bits 14-31: Reserved */
+#define ENET_MRBR_SHIFT (4) /* Bits 4-11: Receive buffer size in bytes */
+#define ENET_MRBR_MASK (0x7f << ENET_MRBR_SHIFT)
+ /* Bits 0-3: Reserved */
+
+/* Receive FIFO Section Full Threshold */
+
+/* Bits 8-31: Reserved */
+#define ENET_RSFL_SHIFT (0) /* Bits 0-7: Value of receive FIFO section full threshold */
+#define ENET_RSFL_MASK (0xff << ENET_RSFL_SHIFT)
+
+/* Receive FIFO Section Empty Threshold */
+
+#define ENET_RSEM_SHIFT (0) /* Bits 0-7: Value of the receive FIFO section empty threshold */
+#define ENET_RSEM_MASK (0xff << ENET_RSEM_SHIFT)
+ /* Bits 8-31: Reserved */
+
+/* Receive FIFO Almost Empty Threshold */
+
+#define ENET_RAEM_SHIFT (0) /* Bits 0-7: Value of the receive FIFO almost empty threshold */
+#define ENET_RAEM_MASK (0xff << ENET_RAEM_SHIFT)
+ /* Bits 8-31: Reserved */
+
+/* Receive FIFO Almost Full Threshold */
+
+#define ENET_RAFL_SHIFT (0) /* Bits 0-7: Value of the receive FIFO almost full threshold */
+#define ENET_RAFL_MASK (0xff << ENET_RAFL_SHIFT)
+ /* Bits 8-31: Reserved */
+
+/* Transmit FIFO Section Empty Threshold */
+
+#define ENET_TSEM_SHIFT (0) /* Bits 0-7: Value of the transmit FIFO section empty threshold */
+#define ENET_TSEM_MASK (0xff << ENET_TSEM_SHIFT)
+ /* Bits 8-31: Reserved */
+
+/* Transmit FIFO Almost Empty Threshold */
+
+#define ENET_TAEM_SHIFT (0) /* Bits 0-7: Value of the transmit FIFO section empty threshold */
+#define ENET_TAEM_MASK (0xff << ENET_TAEM_SHIFT)
+ /* Bits 8-31: Reserved */
+
+/* Transmit FIFO Almost Full Threshold */
+
+#define ENET_TAFL_SHIFT (0) /* Bits 0-7: Value of the transmit FIFO section empty threshold */
+#define ENET_TAFL_MASK (0xff << ENET_TAFL_SHIFT)
+ /* Bits 8-31: Reserved */
+
+/* Transmit Inter-Packet Gap */
+
+#define ENET_TIPG_SHIFT (0) /* Bits 0-4: Value of the transmit FIFO section empty threshold */
+#define ENET_TIPG_MASK (31 << ENET_TIPG_SHIFT)
+ /* Bits 5-31: Reserved */
+
+/* Frame Truncation Length */
+
+#define ENET_FTRL_SHIFT (0) /* Bits 0-13: Value of the transmit FIFO section empty threshold */
+#define ENET_FTRL_MASK (0x3fff << ENET_FTRL_SHIFT)
+ /* Bits 14-31: Reserved */
+
+/* Transmit Accelerator Function Configuration */
+
+#define ENET_TACC_SHIFT16 (1 << 0) /* Bit 0: TX FIFO shift-16 */
+ /* Bits 1-2: Reserved */
+#define ENET_TACC_IPCHK (1 << 3) /* Bit 3: Enables insertion of IP header checksum */
+#define ENET_TACC_PROCHK (1 << 4) /* Bit 4: Enables insertion of protocol checksum */
+ /* Bits 5-31: Reserved */
+
+/* Receive Accelerator Function Configuration */
+
+#define ENET_RACC_PADREM (1 << 0) /* Bit 0: Enable padding removal for short IP frames */
+#define ENET_RACC_IPDIS (1 << 1) /* Bit 1: Enable discard of frames with wrong IPv4 header checksum */
+#define ENET_RACC_PRODIS (1 << 2) /* Bit 2: Enable discard of frames with wrong protocol checksum */
+ /* Bits 3-5: Reserved */
+#define ENET_RACC_LINEDIS (1 << 6) /* Bit 6: Enable discard of frames with MAC layer errors */
+#define ENET_RACC_SHIFT16 (1 << 7) /* Bit 7: RX FIFO shift-16 */
+ /* Bits 8-31: Reserved */
+
+/* Timer Control Register */
+
+#define ENET_ATCR_EN (1 << 0) /* Bit 0: Enable timer */
+ /* Bit 1: Reserved */
+#define ENET_ATCR_OFFEN (1 << 2) /* Bit 2: Enable one-shot offset event */
+#define ENET_ATCR_OFFRST (1 << 3) /* Bit 3: Reset timer on offset event */
+#define ENET_ATCR_PEREN (1 << 4) /* Bit 4: Enable periodical event */
+ /* Bits 5-6: Reserved */
+#define ENET_ATCR_PINPER (1 << 7) /* Bit 7: Enables event signal output assertion on period event */
+ /* Bit 8: Reserved */
+#define ENET_ATCR_RESTART (1 << 9) /* Bit 9: Reset timer */
+ /* Bit 10: Reserved */
+#define ENET_ATCR_CAPTURE (1 << 11) /* Bit 11: Capture timer value */
+ /* Bit 12: Reserved */
+#define ENET_ATCR_SLAVE (1 << 13) /* Bit 13: Enable timer slave mode */
+ /* Bits 14-31: Reserved */
+
+/* Timer Value Register (32-bit timer value)
+ * Timer Offset Register (32-bit offset value)
+ * Timer Period Register (32-bit timer period)
+ */
+
+/* Timer Correction Register */
+
+#define ENET_ATCOR_MASK (0x7fffffff) /* Bits 0-3: Correction counter wrap-around value */
+ /* Bit 31: Reserved */
+
+/* Time-Stamping Clock Period Register */
+
+#define ENET_ATINC_INC_SHIFT (0) /* Bits 0-6: Clock period of the timestamping clock (ts_clk) in nanoseconds */
+#define ENET_ATINC_INC_MASK (0x7f << ENET_ATINC_INC_SHIFT)
+ /* Bit 7: Reserved */
+#define ENET_ATINC_INC_CORR_SHIFT (8) /* Bits 8-14: Correction increment value */
+#define ENET_ATINC_INC_CORR_MASK (0x7f << ENET_ATINC_INC_CORR_SHIFT)
+ /* Bits 15-31: Reserved */
+
+/* Timestamp of Last Transmitted Frame (32-bit timestamp) */
+
+/* Timer Global Status Register */
+
+#define ENET_TGSR_TF0 (1 << 0) /* Bit 0: Copy of Timer Flag for channel 0 */
+#define ENET_TGSR_TF1 (1 << 1) /* Bit 1: Copy of Timer Flag for channel 1 */
+#define ENET_TGSR_TF2 (1 << 2) /* Bit 2: Copy of Timer Flag for channel 2 */
+#define ENET_TGSR_TF3 (1 << 3) /* Bit 3: Copy of Timer Flag for channel 3 */
+ /* Bits 14-31: Reserved */
+
+/* Timer Control Status Register n */
+
+#define ENET_TCSR_TDRE (1 << 0) /* Bit 0: Timer DMA Request Enable */
+ /* Bit 1: Reserved */
+#define ENET_TCSR_TMODE_SHIFT (2) /* Bits 2-5: Timer Mode */
+#define ENET_TCSR_TMODE_MASK (15 << ENET_TCSR_TMODE_SHIFT)
+#define ENET_TCSR_TMODE_DISABLED (0 << ENET_TCSR_TMODE_SHIFT) /* Disabled */
+#define ENET_TCSR_TMODE_ICRISING (1 << ENET_TCSR_TMODE_SHIFT) /* Input Capture on rising edge */
+#define ENET_TCSR_TMODE_ICFALLLING (2 << ENET_TCSR_TMODE_SHIFT) /* Input Capture on falling edge */
+#define ENET_TCSR_TMODE_ICBOTH (3 << ENET_TCSR_TMODE_SHIFT) /* Input Capture on both edges */
+#define ENET_TCSR_TMODE_OCSW (4 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, S/W only */
+#define ENET_TCSR_TMODE_OCTOGGLE (5 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, toggle on compare */
+#define ENET_TCSR_TMODE_OCCLR (6 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, clear on compare */
+#define ENET_TCSR_TMODE_OCSET (7 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, set on compare */
+#define ENET_TCSR_TMODE_OCSETCLR (9 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, set on compare, clear on overflow */
+#define ENET_TCSR_TMODE_OCCLRSET (10 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, clear on compare, set on overflow */
+#define ENET_TCSR_TMODE_PCPULSEL (14 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, pulse low on compare */
+#define ENET_TCSR_TMODE_PCPULSEH (15 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, pulse high on compare */
+
+#define ENET_TCSR_TIE (1 << 6) /* Bit 6: Timer interrupt enable */
+#define ENET_TCSR_TF (1 << 7) /* Bit 7: Timer Flag */
+ /* Bits 8-31: Reserved */
+
+/* Timer Compare Capture Register (32-bit compare value) */
+
+/* Buffer Descriptors ***************************************************************/
+
+/* Endian-independent descriptor offsets */
+
+#define DESC_STATUS1_OFFSET (0)
+#define DESC_LENGTH_OFFSET (2)
+#define DESC_DATAPTR_OFFSET (4)
+#define DESC_LEGACY_LEN (8)
+
+#define DESC_STATUS2_OFFSET (8)
+#define DESC_LENPROTO_OFFSET (12)
+#define DESC_CHECKSUM_OFFSET (14)
+#define DESC_BDU_OFFSET (16)
+#define DESC_TIMESTAMP_OFFSET (20)
+#define DESC_ENHANCED_LEN (32)
+
+/* Legacy/Common TX Buffer Descriptor Bit Definitions.
+ *
+ * The descriptors are represented by structures Unfortunately, when the
+ * structures are overlaid on the data, the bytes are reversed because
+ * the underlying hardware writes the data in big-endian byte order.
+ */
+
+#ifdef IMX_ENET_HAS_DBSWAP
+# ifndef CONFIG_ENDIAN_BIG
+# define IMX_USE_DBSWAP
+# endif
+#else
+# ifndef CONFIG_ENDIAN_BIG
+# define IMX_BUFFERS_SWAP
+# endif
+#endif
+
+#ifndef IMX_BUFFERS_SWAP
+# define TXDESC_ABC (1 << 9) /* Legacy */
+# define TXDESC_TC (1 << 10) /* Common */
+# define TXDESC_L (1 << 11) /* Common */
+# define TXDESC_TO2 (1 << 12) /* Common */
+# define TXDESC_W (1 << 13) /* Common */
+# define TXDESC_TO1 (1 << 14) /* Common */
+# define TXDESC_R (1 << 15) /* Common */
+#else
+# define TXDESC_ABC (1 << 1) /* Legacy */
+# define TXDESC_TC (1 << 2) /* Common */
+# define TXDESC_L (1 << 3) /* Common */
+# define TXDESC_TO2 (1 << 4) /* Common */
+# define TXDESC_W (1 << 5) /* Common */
+# define TXDESC_TO1 (1 << 6) /* Common */
+# define TXDESC_R (1 << 7) /* Common */
+#endif
+
+/* Enhanced (only) TX Buffer Descriptor Bit Definitions */
+
+#ifndef IMX_BUFFERS_SWAP
+# define TXDESC_TSE (1 << 8)
+# define TXDESC_OE (1 << 9)
+# define TXDESC_LCE (1 << 10)
+# define TXDESC_FE (1 << 11)
+# define TXDESC_EE (1 << 12)
+# define TXDESC_UE (1 << 13)
+# define TXDESC_TXE (1 << 15)
+
+# define TXDESC_IINS (1 << 27)
+# define TXDESC_PINS (1 << 28)
+# define TXDESC_TS (1 << 29)
+# define TXDESC_INT (1 << 30)
+
+# define TXDESC_BDU (1 << 31)
+#else
+# define TXDESC_IINS (1 << 3)
+# define TXDESC_PINS (1 << 4)
+# define TXDESC_TS (1 << 5)
+# define TXDESC_INT (1 << 6)
+
+# define TXDESC_TSE (1 << 16)
+# define TXDESC_OE (1 << 17)
+# define TXDESC_LCE (1 << 18)
+# define TXDESC_FE (1 << 19)
+# define TXDESC_EE (1 << 20)
+# define TXDESC_UE (1 << 21)
+# define TXDESC_TXE (1 << 23)
+
+# define TXDESC_BDU (1 << 7)
+#endif
+
+/* Legacy (and Common) RX Buffer Descriptor Bit Definitions */
+
+#ifndef IMX_BUFFERS_SWAP
+# define RXDESC_TR (1 << 0)
+# define RXDESC_OV (1 << 1)
+# define RXDESC_CR (1 << 2)
+# define RXDESC_NO (1 << 4)
+# define RXDESC_LG (1 << 5)
+# define RXDESC_MC (1 << 6)
+# define RXDESC_BC (1 << 7)
+# define RXDESC_M (1 << 8)
+# define RXDESC_L (1 << 11)
+# define RXDESC_R02 (1 << 12)
+# define RXDESC_W (1 << 13)
+# define RXDESC_R01 (1 << 14)
+# define RXDESC_E (1 << 15)
+#else
+# define RXDESC_M (1 << 0)
+# define RXDESC_L (1 << 3)
+# define RXDESC_R02 (1 << 4)
+# define RXDESC_W (1 << 5)
+# define RXDESC_R01 (1 << 6)
+# define RXDESC_E (1 << 7)
+# define RXDESC_TR (1 << 8)
+# define RXDESC_OV (1 << 9)
+# define RXDESC_CR (1 << 10)
+# define RXDESC_NO (1 << 12)
+# define RXDESC_LG (1 << 13)
+# define RXDESC_MC (1 << 14)
+# define RXDESC_BC (1 << 15)
+#endif
+
+/* Enhanced (only) TX Buffer Descriptor Bit Definitions */
+
+#ifndef IMX_BUFFERS_SWAP
+# define RXDESC_FRAG (1 << 0)
+# define RXDESC_IPV6 (1 << 1)
+# define RXDESC_VLAN (1 << 2)
+# define RXDESC_PCR (1 << 4)
+# define RXDESC_ICE (1 << 5)
+# define RXDESC_INT (1 << 23)
+# define RXDESC_UC (1 << 24)
+# define RXDESC_CE (1 << 25)
+# define RXDESC_PE (1 << 26)
+# define RXDESC_ME (1 << 31)
+
+# define RXDESC_BDU (1 << 31)
+#else
+# define RXDESC_UC (1 << 0)
+# define RXDESC_CE (1 << 1)
+# define RXDESC_PE (1 << 2)
+# define RXDESC_ME (1 << 7)
+# define RXDESC_INT (1 << 15)
+# define RXDESC_FRAG (1 << 24)
+# define RXDESC_IPV6 (1 << 25)
+# define RXDESC_VLAN (1 << 26)
+# define RXDESC_PCR (1 << 28)
+# define RXDESC_ICE (1 << 29)
+
+# define RXDESC_BDU (1 << 7)
+#endif
+
+/************************************************************************************
+ * Public Types
+ ************************************************************************************/
+
+/* Buffer Descriptors ***************************************************************/
+
+/* Legacy Buffer Descriptor */
+
+#ifdef CONFIG_ENET_ENHANCEDBD
+#ifdef IMX_USE_DBSWAP
+/* When DBSWP is used to swap the bytes in hardware, it is done 32-bits
+ * at a time. Therefore, all 16 bit elements need to be swapped to
+ * compensate.
+ */
+
+struct enet_desc_s
+{
+ uint16_t length; /* Data length */
+ uint16_t status1; /* Control and status */
+ uint8_t *data; /* Buffer address */
+ uint32_t status2; /* Extended status */
+ uint16_t checksum; /* Payload checksum */
+ uint16_t lenproto; /* Header length + Protocol type */
+ uint32_t bdu; /* BDU */
+ uint32_t timestamp; /* Time stamp */
+ uint32_t reserved1; /* unused */
+ uint32_t reserved2; /* unused */
+};
+#else
+struct enet_desc_s
+{
+ uint16_t status1; /* Control and status */
+ uint16_t length; /* Data length */
+ uint8_t *data; /* Buffer address */
+ uint32_t status2; /* Extended status */
+ uint16_t lenproto; /* Header length + Protocol type */
+ uint16_t checksum; /* Payload checksum */
+ uint32_t bdu; /* BDU */
+ uint32_t timestamp; /* Time stamp */
+ uint32_t reserved1; /* unused */
+ uint32_t reserved2; /* unused */
+};
+#endif /* IMX_USE_DBSWAP */
+#else /* CONFIG_ENET_ENHANCEDBD */
+#ifdef IMX_USE_DBSWAP
+struct enet_desc_s
+{
+ uint16_t length; /* Data length */
+ uint16_t status1; /* Control and status */
+ uint8_t *data; /* Buffer address */
+};
+#else
+struct enet_desc_s
+{
+ uint16_t status1; /* Control and status */
+ uint16_t length; /* Data length */
+ uint8_t *data; /* Buffer address */
+};
+#endif /* IMX_USE_DBSWAP */
+#endif /* CONFIG_ENET_ENHANCEDBD */
+
+#endif /* __ARCH_ARM_SRC_IMX6_HARDWARE_IMX_ENET_H */
diff --git a/arch/arm/src/imx6/imx_enet.c b/arch/arm/src/imx6/imx_enet.c
new file mode 100644
index 0000000..e460e6e
--- /dev/null
+++ b/arch/arm/src/imx6/imx_enet.c
@@ -0,0 +1,2549 @@
+/****************************************************************************
+ * arch/arm/src/imx6/imx_enet.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/wdog.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/phy.h>
+#include <nuttx/net/netdev.h>
+
+#ifdef CONFIG_NET_PKT
+# include <nuttx/net/pkt.h>
+#endif
+
+#include "arm_arch.h"
+#include "gic.h"
+#include "chip.h"
+#include "imx_config.h"
+#include "hardware/imx_enet.h"
+#include "hardware/imx_ccm.h"
+#include "hardware/imx_pinmux.h"
+
+#if 0
+#include "imx_periphclks.h"
+#endif
+
+#include "imx_gpio.h"
+#include "imx_enet.h"
+
+#ifdef CONFIG_IMX6_ENET
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* If processing is not done at the interrupt level, then work queue support
+ * is required.
+ */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+# error Work queue support is required
+#else
+
+ /* Select work queue. Always use the LP work queue if available. If not,
+ * then LPWORK will re-direct to the HP work queue.
+ *
+ * NOTE: However, the network should NEVER run on the high priority work
+ * queue! That queue is intended only to service short back end interrupt
+ * processing that never suspends. Suspending the high priority work queue
+ * may bring the system to its knees!
+ */
+
+# define ETHWORK LPWORK
+#endif
+
+/* CONFIG_IMX_ENET_NETHIFS determines the number of physical interfaces
+ * that will be supported.
+ */
+
+#if CONFIG_IMX_ENET_NETHIFS != 1
+# error "CONFIG_IMX_ENET_NETHIFS must be one for now"
+#endif
+
+#if CONFIG_IMX_ENET_NTXBUFFERS < 1
+# error "Need at least one TX buffer"
+#endif
+
+#if CONFIG_IMX_ENET_NRXBUFFERS < 1
+# error "Need at least one RX buffer"
+#endif
+
+#define NENET_NBUFFERS \
+ (CONFIG_IMX_ENET_NTXBUFFERS + CONFIG_IMX_ENET_NRXBUFFERS)
+
+/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per
+ * second.
+ */
+
+#define IMX_WDDELAY (1 * CLK_TCK)
+
+/* Align assuming that the D-Cache is enabled (probably 32-bytes).
+ *
+ * REVISIT: The size of descriptors and buffers must also be in even units
+ * of the cache line size That is because the operations to clean and
+ * invalidate the cache will operate on a full 32-byte cache line. If
+ * CONFIG_ENET_ENHANCEDBD is selected, then the size of the descriptor is
+ * 32-bytes (and probably already the correct size for the cache line);
+ * otherwise, the size of the descriptors much smaller, only 8 bytes.
+ */
+
+#define ENET_ALIGN 32 /* TODO */
+#define ENET_ALIGN_MASK (ENET_ALIGN - 1)
+#define ENET_ALIGN_UP(n) (((n) + ENET_ALIGN_MASK) & ~ENET_ALIGN_MASK)
+
+/* TX timeout = 1 minute */
+
+#define IMX_TXTIMEOUT (60 * CLK_TCK)
+#define MII_MAXPOLLS (0x1ffff)
+#define LINK_WAITUS (500 * 1000)
+#define LINK_NLOOPS (10)
+
+/* PHY definitions.
+ *
+ * The selected PHY must be selected from the drivers/net/Kconfig PHY menu.
+ * A description of the PHY must be provided here. That description must
+ * include:
+ *
+ * 1. BOARD_PHY_NAME: A PHY name string (for debug output),
+ * 2. BOARD_PHYID1 and BOARD_PHYID2: The PHYID1 and PHYID2 values (from
+ * include/nuttx/net/mii.h)
+ * 3. BOARD_PHY_STATUS: The address of the status register to use when
+ * querying link status (from include/nuttx/net/mii.h)
+ * 4. BOARD_PHY_ADDRThe PHY broadcast address of 0 is selected. This
+ * should be fine as long as there is only a single PHY.
+ * 5. BOARD_PHY_10BASET: A macro that can convert the status register
+ * value into a boolean: true=10Base-T, false=Not 10Base-T
+ * 6. BOARD_PHY_100BASET: A macro that can convert the status register
+ * value into a boolean: true=100Base-T, false=Not 100Base-T
+ * 7. BOARD_PHY_ISDUPLEX: A macro that can convert the status register
+ * value into a boolean: true=duplex mode, false=half-duplex mode
+ *
+ */
+
+/* TODO:
+ * KSZ9021RN gigabit ethernet phy for sabrelite
+ * AR8031 gigabit ethernet phy for sabre-6quad
+ */
+
+#if defined(CONFIG_ETH0_PHY_KSZ8081)
+# define BOARD_PHY_NAME "KSZ8081"
+# define BOARD_PHYID1 MII_PHYID1_KSZ8081
+# define BOARD_PHYID2 MII_PHYID2_KSZ8081
+# define BOARD_PHY_STATUS MII_KSZ8081_PHYCTRL1
+# define BOARD_PHY_ADDR (0)
+# define BOARD_PHY_10BASET(s) (((s) & MII_PHYCTRL1_MODE_10HDX) != 0)
+# define BOARD_PHY_100BASET(s) (((s) & MII_PHYCTRL1_MODE_100HDX) != 0)
+# define BOARD_PHY_ISDUPLEX(s) (((s) & MII_PHYCTRL1_MODE_DUPLEX) != 0)
+#elif defined(CONFIG_ETH0_PHY_LAN8720)
+# define BOARD_PHY_NAME "LAN8720"
+# define BOARD_PHYID1 MII_PHYID1_LAN8720
+# define BOARD_PHYID2 MII_PHYID2_LAN8720
+# define BOARD_PHY_STATUS MII_LAN8720_SCSR
+# define BOARD_PHY_ADDR (1)
+# define BOARD_PHY_10BASET(s) (((s)&MII_LAN8720_SPSCR_10MBPS) != 0)
+# define BOARD_PHY_100BASET(s) (((s)&MII_LAN8720_SPSCR_100MBPS) != 0)
+# define BOARD_PHY_ISDUPLEX(s) (((s)&MII_LAN8720_SPSCR_DUPLEX) != 0)
+#else
+# error "Unrecognized or missing PHY selection"
+#endif
+
+/* Estimate the MII_SPEED in order to get an MDC close to 2.5MHz,
+ * based on the internal module (ENET) clock:
+ *
+ * MII_SPEED = ENET_FREQ/5000000 -1
+ *
+ * For example, if ENET_FREQ_MHZ=120 (MHz):
+ *
+ * MII_SPEED = 120000000/5000000 -1
+ * = 23
+ */
+
+#define IMX_MII_SPEED 0x38 /* 100Mbs. Revisit and remove hardcoded value */
+#if IMX_MII_SPEED > 63
+# error "IMX_MII_SPEED is out-of-range"
+#endif
+
+/* Interrupt groups */
+
+#define RX_INTERRUPTS (ENET_INT_RXF | ENET_INT_RXB)
+#define TX_INTERRUPTS ENET_INT_TXF
+#define ERROR_INTERRUPTS (ENET_INT_UN | ENET_INT_RL | ENET_INT_LC | \
+ ENET_INT_EBERR | ENET_INT_BABT | ENET_INT_BABR)
+
+/* The subset of errors that require us to reset the hardware - this list
+ * may need to be revisited if it's found that some error above leads to a
+ * locking up of the Ethernet interface.
+ */
+
+#define CRITICAL_ERROR (ENET_INT_UN | ENET_INT_RL | ENET_INT_EBERR)
+
+/* This is a helper pointer for accessing
+ * the contents of the Ethernet header
+ */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+#define IMX_BUF_SIZE ENET_ALIGN_UP(CONFIG_NET_ETH_PKTSIZE)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The imx_driver_s encapsulates all state information for a single hardware
+ * interface
+ */
+
+struct imx_driver_s
+{
+ bool bifup; /* true:ifup false:ifdown */
+ uint8_t txtail; /* The oldest busy TX descriptor */
+ uint8_t txhead; /* The next TX descriptor to use */
+ uint8_t rxtail; /* The next RX descriptor to use */
+ uint8_t phyaddr; /* Selected PHY address */
+ struct wdog_s txpoll; /* TX poll timer */
+ struct wdog_s txtimeout; /* TX timeout timer */
+ struct work_s irqwork; /* For deferring interrupt work to the work queue */
+ struct work_s pollwork; /* For deferring poll work to the work queue */
+ struct enet_desc_s *txdesc; /* A pointer to the list of TX descriptor */
+ struct enet_desc_s *rxdesc; /* A pointer to the list of RX descriptors */
+
+ /* This holds the information visible to the NuttX network */
+
+ struct net_driver_s dev; /* Interface understood by the network */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct imx_driver_s g_enet[CONFIG_IMX_ENET_NETHIFS];
+
+/* The DMA descriptors. A unaligned uint8_t is used to allocate the
+ * memory; 16 is added to assure that we can meet the descriptor alignment
+ * requirements.
+ */
+
+static uint8_t g_desc_pool[NENET_NBUFFERS * sizeof(struct enet_desc_s)]
+ __attribute__((aligned(ENET_ALIGN)));
+
+/* The DMA buffers. Again, A unaligned uint8_t is used to allocate the
+ * memory; 16 is added to assure that we can meet the descriptor alignment
+ * requirements.
+ */
+
+static uint8_t g_buffer_pool[NENET_NBUFFERS * IMX_BUF_SIZE]
+ __attribute__((aligned(ENET_ALIGN)));
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Utility functions */
+
+#ifndef IMXRT_BUFFERS_SWAP
+# define imx_swap32(value) (value)
+# define imx_swap16(value) (value)
+#else
+#if 0 /* Use builtins if the compiler supports them */
+static inline uint32_t imx_swap32(uint32_t value);
+static inline uint16_t imx_swap16(uint16_t value);
+#else
+# define imx_swap32 __builtin_bswap32
+# define imx_swap16 __builtin_bswap16
+#endif
+#endif
+
+/* Common TX logic */
+
+static bool imx_txringfull(FAR struct imx_driver_s *priv);
+static int imx_transmit(FAR struct imx_driver_s *priv);
+static int imx_txpoll(struct net_driver_s *dev);
+
+/* Interrupt handling */
+
+static void imx_dispatch(FAR struct imx_driver_s *priv);
+static void imx_receive(FAR struct imx_driver_s *priv);
+static void imx_txdone(FAR struct imx_driver_s *priv);
+
+static void imx_enet_interrupt_work(FAR void *arg);
+static int imx_enet_interrupt(int irq, FAR void *context, FAR void *arg);
+
+/* Watchdog timer expirations */
+
+static void imx_txtimeout_work(FAR void *arg);
+static void imx_txtimeout_expiry(wdparm_t arg);
+
+static void imx_poll_work(FAR void *arg);
+static void imx_polltimer_expiry(wdparm_t arg);
+
+/* NuttX callback functions */
+
+static int imx_ifup(struct net_driver_s *dev);
+static int imx_ifdown(struct net_driver_s *dev);
+
+static void imx_txavail_work(FAR void *arg);
+static int imx_txavail(struct net_driver_s *dev);
+
+/* Internal ifup function that allows phy reset to be optional */
+
+static int imx_ifup_action(struct net_driver_s *dev, bool resetphy);
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int imx_addmac(struct net_driver_s *dev,
+ FAR const uint8_t *mac);
+static int imx_rmmac(struct net_driver_s *dev, FAR const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int imx_ioctl(struct net_driver_s *dev, int cmd,
+ unsigned long arg);
+#endif
+
+/* PHY/MII support */
+
+#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT)
+static int imx_phyintenable(struct imx_driver_s *priv);
+#endif
+static inline void imx_initmii(struct imx_driver_s *priv);
+
+#if 0 /* TODO */
+static int imx_writemii(struct imx_driver_s *priv, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t data);
+static int imx_readmii(struct imx_driver_s *priv, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t *data);
+#endif
+
+static int imx_initphy(struct imx_driver_s *priv, bool renogphy);
+
+/* Initialization */
+
+static void imx_initbuffers(struct imx_driver_s *priv);
+static void imx_reset(struct imx_driver_s *priv);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: imx_txringfull
+ *
+ * Description:
+ * Check if all of the TX descriptors are in use.
+ *
+ * Input Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * true is the TX ring is full; false if there are free slots at the
+ * head index.
+ *
+ ****************************************************************************/
+
+static bool imx_txringfull(FAR struct imx_driver_s *priv)
+{
+ uint8_t txnext;
+
+ /* Check if there is room in the hardware to hold another outgoing
+ * packet. The ring is full if incrementing the head pointer would
+ * collide with the tail pointer.
+ */
+
+ txnext = priv->txhead + 1;
+ if (txnext >= CONFIG_IMX_ENET_NTXBUFFERS)
+ {
+ txnext = 0;
+ }
+
+ return priv->txtail == txnext;
+}
+
+/****************************************************************************
+ * Function: imx_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int imx_transmit(FAR struct imx_driver_s *priv)
+{
+ struct enet_desc_s *txdesc;
+ irqstate_t flags;
+ uint32_t regval;
+ uint8_t *buf;
+
+ /* Since this can be called from imx_receive, it is possible that
+ * the transmit queue is full, so check for that now. If this is the
+ * case, the outgoing packet will be dropped (e.g. an ARP reply)
+ */
+
+ if (imx_txringfull(priv))
+ {
+ return -EBUSY;
+ }
+
+ /* When we get here the TX descriptor should show that the previous
+ * transfer has completed. If we get here, then we are committed to
+ * sending a packet; Higher level logic must have assured that there is
+ * no transmission in progress.
+ */
+
+ txdesc = &priv->txdesc[priv->txhead];
+ priv->txhead++;
+ if (priv->txhead >= CONFIG_IMX_ENET_NTXBUFFERS)
+ {
+ priv->txhead = 0;
+ }
+
+#ifdef CONFIG_DEBUG_ASSERTIONS
+ up_invalidate_dcache((uintptr_t)txdesc,
+ (uintptr_t)txdesc + sizeof(struct enet_desc_s));
+
+ DEBUGASSERT(priv->txtail != priv->txhead &&
+ (txdesc->status1 & TXDESC_R) == 0);
+#endif
+
+ /* Increment statistics */
+
+ NETDEV_TXPACKETS(&priv->dev);
+
+ /* Setup the buffer descriptor for transmission: address=priv->dev.d_buf,
+ * length=priv->dev.d_len
+ */
+
+ txdesc->length = imx_swap16(priv->dev.d_len);
+#ifdef CONFIG_IMX_ENETENHANCEDBD
+ txdesc->bdu = 0x00000000;
+ txdesc->status2 = TXDESC_INT | TXDESC_TS; /* | TXDESC_IINS | TXDESC_PINS; */
+#endif
+ txdesc->status1 |= (TXDESC_R | TXDESC_L | TXDESC_TC);
+
+ buf = (uint8_t *)imx_swap32((uint32_t)priv->dev.d_buf);
+ if (priv->rxdesc[priv->rxtail].data == buf)
+ {
+ struct enet_desc_s *rxdesc = &priv->rxdesc[priv->rxtail];
+
+ /* Data was written into the RX buffer, so swap the TX and RX buffers */
+
+ DEBUGASSERT((rxdesc->status1 & RXDESC_E) == 0);
+ rxdesc->data = txdesc->data;
+ txdesc->data = buf;
+ }
+ else
+ {
+ DEBUGASSERT(txdesc->data == buf);
+ }
+
+ /* Make the following operations atomic */
+
+ flags = spin_lock_irqsave();
+
+ /* Enable TX interrupts */
+
+ regval = getreg32(IMX_ENET_EIMR);
+ regval |= TX_INTERRUPTS;
+ putreg32(regval, IMX_ENET_EIMR);
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ wd_start(&priv->txtimeout, IMX_TXTIMEOUT,
+ imx_txtimeout_expiry, (wdparm_t)priv);
+
+ /* Start the TX transfer (if it was not already waiting for buffers) */
+
+ putreg32(ENET_TDAR, IMX_ENET_TDAR);
+
+ spin_unlock_irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: imx_txpoll
+ *
+ * Description:
+ * The transmitter is available, check if the network has any outgoing
+ * packets ready to send. This is a callback from devif_poll().
+ * devif_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete,
+ * 2. When the preceding TX packet send timesout and the interface is reset
+ * 3. During normal TX polling
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int imx_txpoll(struct net_driver_s *dev)
+{
+ FAR struct imx_driver_s *priv =
+ (FAR struct imx_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.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ /* Look up the destination MAC address and add it to the Ethernet
+ * header.
+ */
+
+#ifdef CONFIG_NET_IPv4
+#ifdef CONFIG_NET_IPv6
+ if (IFF_IS_IPv4(priv->dev.d_flags))
+#endif
+ {
+ arp_out(&priv->dev);
+ }
+#endif /* CONFIG_NET_IPv4 */
+
+#ifdef CONFIG_NET_IPv6
+#ifdef CONFIG_NET_IPv4
+ else
+#endif
+ {
+ neighbor_out(&priv->dev);
+ }
+#endif /* CONFIG_NET_IPv6 */
+
+ if (!devif_loopback(&priv->dev))
+ {
+ /* Send the packet */
+
+ imx_transmit(priv);
+ priv->dev.d_buf = (uint8_t *)
+ imx_swap32((uint32_t)priv->txdesc[priv->txhead].data);
+
+ /* Check if there is room in the device to hold another packet. If
+ * not, return a non-zero value to terminate the poll.
+ */
+
+ if (imx_txringfull(priv))
+ {
+ return -EBUSY;
+ }
+ }
+ }
+
+ /* If zero is returned, the polling will continue until
+ * all connections have been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: imx_dispatch
+ *
+ * Description:
+ * A new Rx packet was received; dispatch that packet to the network layer
+ * as necessary.
+ *
+ * Input Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static inline void imx_dispatch(FAR struct imx_driver_s *priv)
+{
+ /* Update statistics */
+
+ NETDEV_RXPACKETS(&priv->dev);
+
+#ifdef CONFIG_NET_PKT
+ /* When packet sockets are enabled, feed the frame into the packet tap */
+
+ pkt_input(&priv->dev);
+#endif
+
+#ifdef CONFIG_NET_IPv4
+ /* Check for an IPv4 packet */
+
+ if (BUF->type == HTONS(ETHTYPE_IP))
+ {
+ ninfo("IPv4 frame\n");
+ NETDEV_RXIPV4(&priv->dev);
+
+ /* Handle ARP on input then give the IPv4 packet to the network
+ * layer
+ */
+
+ arp_ipin(&priv->dev);
+ ipv4_input(&priv->dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ /* Update the Ethernet header with the correct MAC address */
+
+#ifdef CONFIG_NET_IPv6
+ if (IFF_IS_IPv4(priv->dev.d_flags))
+#endif
+ {
+ arp_out(&priv->dev);
+ }
+#ifdef CONFIG_NET_IPv6
+ else
+ {
+ neighbor_out(&priv->dev);
+ }
+#endif
+
+ /* And send the packet */
+
+ imx_transmit(priv);
+ }
+ }
+ else
+#endif
+#ifdef CONFIG_NET_IPv6
+ /* Check for an IPv6 packet */
+
+ if (BUF->type == HTONS(ETHTYPE_IP6))
+ {
+ ninfo("Iv6 frame\n");
+ NETDEV_RXIPV6(&priv->dev);
+
+ /* Give the IPv6 packet to the network layer */
+
+ ipv6_input(&priv->dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ /* Update the Ethernet header with the correct MAC address */
+
+#ifdef CONFIG_NET_IPv4
+ if (IFF_IS_IPv4(priv->dev.d_flags))
+ {
+ arp_out(&priv->dev);
+ }
+ else
+#endif
+#ifdef CONFIG_NET_IPv6
+ {
+ neighbor_out(&priv->dev);
+ }
+#endif
+
+ /* And send the packet */
+
+ imx_transmit(priv);
+ }
+ }
+ else
+#endif
+#ifdef CONFIG_NET_ARP
+ /* Check for an ARP packet */
+
+ if (BUF->type == htons(ETHTYPE_ARP))
+ {
+ NETDEV_RXARP(&priv->dev);
+ 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)
+ {
+ imx_transmit(priv);
+ }
+ }
+#endif
+ else
+ {
+ NETDEV_RXDROPPED(&priv->dev);
+ }
+}
+
+/****************************************************************************
+ * Function: imx_receive
+ *
+ * Description:
+ * An interrupt was received indicating the availability of a new RX packet
+ *
+ * Input Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void imx_receive(FAR struct imx_driver_s *priv)
+{
+ struct enet_desc_s *rxdesc;
+ bool received;
+
+ /* Loop while there are received packets to be processed */
+
+ do
+ {
+ /* Invalidate the Rx descriptor. Since it has been modified via DMA,
+ * we must assure that we must invalid any cached values and re-read
+ * the descriptor from the memory.
+ */
+
+ rxdesc = &priv->rxdesc[priv->rxtail];
+ up_invalidate_dcache((uintptr_t)rxdesc,
+ (uintptr_t)rxdesc + sizeof(struct enet_desc_s));
+
+ /* Check if the data buffer associated with the descriptor has
+ * been filled with valid data.
+ */
+
+ received = ((rxdesc->status1 & RXDESC_E) == 0);
+ if (received)
+ {
+ /* Copy the buffer pointer to priv->dev.d_buf. Set amount of data
+ * in priv->dev.d_len
+ */
+
+ priv->dev.d_len = imx_swap16(rxdesc->length);
+ priv->dev.d_buf = (uint8_t *)imx_swap32((uint32_t)rxdesc->data);
+
+ /* Invalidate the buffer so that the correct packet will be re-read
+ * from memory when the packet content is accessed.
+ */
+
+ up_invalidate_dcache((uintptr_t)priv->dev.d_buf,
+ (uintptr_t)priv->dev.d_buf + priv->dev.d_len);
+
+ /* Dispatch (or drop) the newly received packet */
+
+ imx_dispatch(priv);
+
+ /* Point the packet buffer back to the next Tx buffer that will be
+ * used during the next write. If the write queue is full, then
+ * this will point at an active buffer, which must not be written
+ * to. This is OK because devif_poll won't be called unless the
+ * queue is not full.
+ */
+
+ priv->dev.d_buf = (uint8_t *)
+ imx_swap32((uint32_t)priv->txdesc[priv->txhead].data);
+ rxdesc->status1 |= RXDESC_E;
+
+ /* Update the index to the next descriptor */
+
+ priv->rxtail++;
+ if (priv->rxtail >= CONFIG_IMX_ENET_NRXBUFFERS)
+ {
+ priv->rxtail = 0;
+ }
+
+ /* Indicate that there have been empty receive buffers produced */
+
+ putreg32(ENET_RDAR, IMX_ENET_RDAR);
+ }
+ }
+ while (received);
+}
+
+/****************************************************************************
+ * Function: imx_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Input Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static void imx_txdone(FAR struct imx_driver_s *priv)
+{
+ struct enet_desc_s *txdesc;
+ uint32_t regval;
+ bool txdone;
+
+ /* We are here because a transmission completed, so the watchdog can be
+ * canceled.
+ */
+
+ wd_cancel(&priv->txtimeout);
+
+ /* Verify that the oldest descriptor descriptor completed */
+
+ do
+ {
+ /* Invalidate the Tx descriptor. Since status information has been
+ * modified via DMA, we must assure that we must invalid any cached
+ * values and re-read the descriptor from the memory.
+ */
+
+ txdesc = &priv->txdesc[priv->txtail];
+ up_invalidate_dcache((uintptr_t)txdesc,
+ (uintptr_t)txdesc + sizeof(struct enet_desc_s));
+
+ txdone = false;
+ if ((txdesc->status1 & TXDESC_R) == 0 && priv->txtail != priv->txhead)
+ {
+ /* Yes.. bump up the tail pointer, making space for a new TX
+ * descriptor.
+ */
+
+ priv->txtail++;
+ if (priv->txtail >= CONFIG_IMX_ENET_NTXBUFFERS)
+ {
+ priv->txtail = 0;
+ }
+
+ /* Update statistics */
+
+ NETDEV_TXDONE(&priv->dev);
+ txdone = true;
+ }
+ }
+ while (txdone);
+
+ /* Are there other transmissions queued? */
+
+ if (priv->txtail == priv->txhead)
+ {
+ /* No.. Cancel the TX timeout and disable further Tx interrupts. */
+
+ wd_cancel(&priv->txtimeout);
+
+ regval = getreg32(IMX_ENET_EIMR);
+ regval &= ~TX_INTERRUPTS;
+ putreg32(regval, IMX_ENET_EIMR);
+ }
+
+ /* There should be space for a new TX in any event. Poll the network for
+ * new XMIT data
+ */
+
+ devif_poll(&priv->dev, imx_txpoll);
+}
+
+/****************************************************************************
+ * Function: imx_enet_interrupt_work
+ *
+ * Description:
+ * Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ * arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static void imx_enet_interrupt_work(FAR void *arg)
+{
+ FAR struct imx_driver_s *priv = (FAR struct imx_driver_s *)arg;
+ uint32_t pending;
+#ifdef CONFIG_NET_MCASTGROUP
+ uint32_t gaurstore;
+ uint32_t galrstore;
+#endif
+
+ /* Process pending Ethernet interrupts */
+
+ net_lock();
+
+ /* Get the set of unmasked, pending interrupt. */
+
+ pending = getreg32(IMX_ENET_EIR) & getreg32(IMX_ENET_EIMR);
+
+ /* Clear the pending interrupts */
+
+ putreg32(pending, IMX_ENET_EIR);
+
+ /* Check for errors */
+
+ if (pending & ERROR_INTERRUPTS)
+ {
+ /* An error has occurred, update statistics */
+
+ NETDEV_ERRORS(&priv->dev);
+
+ nerr("ERROR: Network interface error occurred (0x%08X)\n",
+ (pending & ERROR_INTERRUPTS));
+ }
+
+ if (pending & CRITICAL_ERROR)
+ {
+ nerr("Critical error, restarting Ethernet interface\n");
+
+ /* Bring the Ethernet chip down and back up but with no need to
+ * reset/renegotiate the phy.
+ */
+
+#ifdef CONFIG_NET_MCASTGROUP
+ /* Just before we pull the rug lets make sure we retain the
+ * multicast hash table.
+ */
+
+ gaurstore = getreg32(IMX_ENET_GAUR);
+ galrstore = getreg32(IMX_ENET_GALR);
+#endif
+
+ imx_ifdown(&priv->dev);
+ imx_ifup_action(&priv->dev, false);
+
+#ifdef CONFIG_NET_MCASTGROUP
+ /* Now write the multicast table back */
+
+ putreg32(gaurstore, IMX_ENET_GAUR);
+ putreg32(galrstore, IMX_ENET_GALR);
+#endif
+
+ /* Then poll the network for new XMIT data */
+
+ devif_poll(&priv->dev, imx_txpoll);
+ }
+ else
+ {
+ /* Check for the receipt of a packet */
+
+ if ((pending & ENET_INT_RXF) != 0)
+ {
+ /* A packet has been received, call imx_receive() to handle the
+ * packet.
+ */
+
+ imx_receive(priv);
+ }
+
+ /* Check if a packet transmission has completed */
+
+ if ((pending & ENET_INT_TXF) != 0)
+ {
+ /* Call imx_txdone to handle the end of transfer even. NOTE
+ * that this may disable further Tx interrupts if there are no
+ * pending transmissions.
+ */
+
+ imx_txdone(priv);
+ }
+ }
+
+ net_unlock();
+
+ /* Re-enable Ethernet interrupts */
+
+#if 0
+ up_enable_irq(IMX_IRQ_ENET0TMR);
+#endif
+ up_enable_irq(IMX_IRQ_ENET0);
+}
+
+/****************************************************************************
+ * Function: imx_enet_interrupt
+ *
+ * Description:
+ * Three interrupt sources will vector this this function:
+ * 1. Ethernet MAC transmit interrupt handler
+ * 2. Ethernet MAC receive interrupt handler
+ * 3.
+ *
+ * Input 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 imx_enet_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+ register FAR struct imx_driver_s *priv = &g_enet[0];
+
+ /* Disable further Ethernet interrupts. Because Ethernet interrupts are
+ * also disabled if the TX timeout event occurs, there can be no race
+ * condition here.
+ */
+
+ up_disable_irq(IMX_IRQ_ENET0);
+
+ /* Schedule to perform the interrupt processing on the worker thread. */
+
+ work_queue(ETHWORK, &priv->irqwork, imx_enet_interrupt_work, priv, 0);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: imx_txtimeout_work
+ *
+ * Description:
+ * Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ * arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void imx_txtimeout_work(FAR void *arg)
+{
+ FAR struct imx_driver_s *priv = (FAR struct imx_driver_s *)arg;
+
+ /* Increment statistics and dump debug info */
+
+ net_lock();
+ nerr("Resetting interface\n");
+
+ NETDEV_TXTIMEOUTS(&priv->dev);
+
+ /* Take the interface down and bring it back up. That is the most
+ * aggressive hardware reset.
+ */
+
+ imx_ifdown(&priv->dev);
+ imx_ifup_action(&priv->dev, false);
+
+ /* Then poll the network for new XMIT data */
+
+ devif_poll(&priv->dev, imx_txpoll);
+ net_unlock();
+}
+
+/****************************************************************************
+ * Function: imx_txtimeout_expiry
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Input Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void imx_txtimeout_expiry(wdparm_t arg)
+{
+ FAR struct imx_driver_s *priv = (FAR struct imx_driver_s *)arg;
+
+ /* Disable further Ethernet interrupts. This will prevent some race
+ * conditions with interrupt work. There is still a potential race
+ * condition with interrupt work that is already queued and in progress.
+ */
+
+ up_disable_irq(IMX_IRQ_ENET0);
+
+ /* Schedule to perform the TX timeout processing on the worker thread,
+ * canceling any pending interrupt work.
+ */
+
+ work_queue(ETHWORK, &priv->irqwork, imx_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: imx_poll_work
+ *
+ * Description:
+ * Perform periodic polling from the worker thread
+ *
+ * Input Parameters:
+ * arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static void imx_poll_work(FAR void *arg)
+{
+ FAR struct imx_driver_s *priv = (FAR struct imx_driver_s *)arg;
+
+ /* Check if there is there is a transmission in progress.
+ * We cannot perform the TX poll if he are unable to accept
+ * another packet for transmission.
+ */
+
+ net_lock();
+ if (!imx_txringfull(priv))
+ {
+ /* If so, update TCP timing states and poll the network for new XMIT
+ * data. Hmmm.. might be bug here. Does this mean if there is a
+ * transmit in progress, we will missing TCP time state updates?
+ */
+
+ devif_timer(&priv->dev, IMX_WDDELAY, imx_txpoll);
+ }
+
+ /* Setup the watchdog poll timer again in any case */
+
+ wd_start(&priv->txpoll, IMX_WDDELAY,
+ imx_polltimer_expiry, (wdparm_t)priv);
+ net_unlock();
+}
+
+/****************************************************************************
+ * Function: imx_polltimer_expiry
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Input Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void imx_polltimer_expiry(wdparm_t arg)
+{
+ FAR struct imx_driver_s *priv = (FAR struct imx_driver_s *)arg;
+
+ /* Schedule to perform the poll processing on the worker thread. */
+
+ work_queue(ETHWORK, &priv->pollwork, imx_poll_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: imx_ifup_action
+ *
+ * Description:
+ * Internal action routine to bring up the Ethernet interface
+ * which makes the resetting of the phy (which takes considerable time)
+ * optional.
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * resetphy - Flag indicating if Phy is to be reset. If not then the
+ * phy configuration is just re-loaded into the ethernet
+ * interface
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int imx_ifup_action(struct net_driver_s *dev, bool resetphy)
+{
+ FAR struct imx_driver_s *priv =
+ (FAR struct imx_driver_s *)dev->d_private;
+ uint8_t *mac = dev->d_mac.ether.ether_addr_octet;
+ uint32_t regval;
+ int ret;
+
+ ninfo("Bringing up: %d.%d.%d.%d\n",
+ (int)dev->d_ipaddr & 0xff,
+ (int)(dev->d_ipaddr >> 8) & 0xff,
+ (int)(dev->d_ipaddr >> 16) & 0xff,
+ (int)dev->d_ipaddr >> 24);
+
+ /* Initialize ENET buffers */
+
+ imx_initbuffers(priv);
+
+ /* Configure the MII interface */
+
+ imx_initmii(priv);
+
+ /* Set the MAC address */
+
+ putreg32((mac[0] << 24) | (mac[1] << 16) | (mac[2] << 8) | mac[3],
+ IMX_ENET_PALR);
+ putreg32((mac[4] << 24) | (mac[5] << 16), IMX_ENET_PAUR);
+
+ /* Configure the PHY */
+
+ ret = imx_initphy(priv, resetphy);
+ if (ret < 0)
+ {
+ nerr("ERROR: Failed to configure the PHY: %d\n", ret);
+ return ret;
+ }
+
+ /* Handle promiscuous mode */
+
+#ifdef CONFIG_NET_PROMISCUOUS
+ regval = getreg32(IMX_ENET_RCR);
+ regval |= ENET_RCR_PROM;
+ putreg32(regval, IMX_ENET_RCR);
+#endif
+
+ /* Select legacy of enhanced buffer descriptor format */
+
+#ifdef CONFIG_IMX_ENETENHANCEDBD
+ putreg32(ENET_ECR_EN1588, IMX_ENET_ECR);
+#else
+ putreg32(0, IMX_ENET_ECR);
+#endif
+
+ /* Set the RX buffer size */
+
+ putreg32(IMX_BUF_SIZE, IMX_ENET_MRBR);
+
+ /* Point to the start of the circular RX buffer descriptor queue */
+
+ putreg32((uint32_t)priv->rxdesc, IMX_ENET_RDSR);
+
+ /* Point to the start of the circular TX buffer descriptor queue */
+
+ putreg32((uint32_t)priv->txdesc, IMX_ENET_TDSR);
+
+ /* And enable the MAC itself */
+
+ regval = getreg32(IMX_ENET_ECR);
+ regval |= ENET_ECR_ETHEREN
+#ifdef IMX_USE_DBSWAP
+ | ENET_ECR_DBSWP
+#endif
+ ;
+ putreg32(regval, IMX_ENET_ECR);
+
+ /* Indicate that there have been empty receive buffers produced */
+
+ putreg32(ENET_RDAR, IMX_ENET_RDAR);
+
+ /* Set and activate a timer process */
+
+ wd_start(&priv->txpoll, IMX_WDDELAY,
+ imx_polltimer_expiry, (wdparm_t)priv);
+
+ /* Clear all pending ENET interrupt */
+
+ putreg32(RX_INTERRUPTS | ERROR_INTERRUPTS | TX_INTERRUPTS, IMX_ENET_EIR);
+
+ /* Enable RX and error interrupts at the controller (TX interrupts are
+ * still disabled).
+ */
+
+ putreg32(RX_INTERRUPTS | ERROR_INTERRUPTS,
+ IMX_ENET_EIMR);
+
+ /* Mark the interrupt "up" and enable interrupts at the NVIC */
+
+ priv->bifup = true;
+
+#if 0
+ up_enable_irq(IMX_IRQ_ENET0TMR);
+#endif
+ up_enable_irq(IMX_IRQ_ENET0);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: imx_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the Ethernet interface when an IP address is
+ * provided
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int imx_ifup(struct net_driver_s *dev)
+{
+ /* The externally available ifup action includes resetting the phy */
+
+ return imx_ifup_action(dev, true);
+}
+
+/****************************************************************************
+ * Function: imx_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int imx_ifdown(struct net_driver_s *dev)
+{
+ FAR struct imx_driver_s *priv =
+ (FAR struct imx_driver_s *)dev->d_private;
+ irqstate_t flags;
+
+ ninfo("Taking down: %d.%d.%d.%d\n",
+ (int)(dev->d_ipaddr & 0xff),
+ (int)((dev->d_ipaddr >> 8) & 0xff),
+ (int)((dev->d_ipaddr >> 16) & 0xff),
+ (int)(dev->d_ipaddr >> 24));
+
+ /* Flush and disable the Ethernet interrupts at the NVIC */
+
+ flags = enter_critical_section();
+
+ up_disable_irq(IMX_IRQ_ENET0);
+ putreg32(0, IMX_ENET_EIMR);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(&priv->txpoll);
+ wd_cancel(&priv->txtimeout);
+
+ /* Put the EMAC in its reset, non-operational state. This should be
+ * a known configuration that will guarantee the imx_ifup() always
+ * successfully brings the interface back up.
+ */
+
+ imx_reset(priv);
+
+ /* Mark the device "down" */
+
+ priv->bifup = false;
+ leave_critical_section(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: imx_txavail_work
+ *
+ * Description:
+ * Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ * arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void imx_txavail_work(FAR void *arg)
+{
+ FAR struct imx_driver_s *priv = (FAR struct imx_driver_s *)arg;
+
+ /* Ignore the notification if the interface is not yet up */
+
+ net_lock();
+ if (priv->bifup)
+ {
+ /* Check if there is room in the hardware to hold another outgoing
+ * packet.
+ */
+
+ if (!imx_txringfull(priv))
+ {
+ /* No, there is space for another transfer. Poll the network for
+ * new XMIT data.
+ */
+
+ devif_timer(&priv->dev, 0, imx_txpoll);
+ }
+ }
+
+ net_unlock();
+}
+
+/****************************************************************************
+ * Function: imx_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.
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int imx_txavail(struct net_driver_s *dev)
+{
+ FAR struct imx_driver_s *priv =
+ (FAR struct imx_driver_s *)dev->d_private;
+
+ /* Is our single work structure available? It may not be if there are
+ * pending interrupt actions and we will have to ignore the Tx
+ * availability action.
+ */
+
+ if (work_available(&priv->pollwork))
+ {
+ /* Schedule to serialize the poll on the worker thread. */
+
+ work_queue(ETHWORK, &priv->pollwork, imx_txavail_work, priv, 0);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: imx_calcethcrc
+ *
+ * Description:
+ * Function to calculate the CRC used by IMX to check an Ethernet frame
+ *
+ * Input Parameters:
+ * data - the data to be checked
+ * length - length of the data
+ *
+ * Returned Value:
+ * crc32
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static uint32_t imx_calcethcrc(const uint8_t *data, size_t length)
+{
+ uint32_t crc = 0xffffffffu;
+ uint32_t count1 = 0;
+ uint32_t count2 = 0;
+
+ /* Calculates the CRC-32 polynomial on the multicast group address. */
+
+ for (count1 = 0; count1 < length; count1++)
+ {
+ uint8_t c = data[count1];
+
+ for (count2 = 0; count2 < 0x08u; count2++)
+ {
+ if ((c ^ crc) & 1U)
+ {
+ crc >>= 1U;
+ c >>= 1U;
+ crc ^= 0xedb88320u;
+ }
+ else
+ {
+ crc >>= 1U;
+ c >>= 1U;
+ }
+ }
+ }
+
+ return crc;
+}
+#endif
+
+/****************************************************************************
+ * Function: imx_enet_hash_index
+ *
+ * Description:
+ * Function to find the hash index for multicast address filter
+ *
+ * Input Parameters:
+ * mac - The MAC address
+ *
+ * Returned Value:
+ * hash index
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static uint32_t imx_enet_hash_index(const uint8_t *mac)
+{
+ uint32_t crc;
+ uint32_t hashindex;
+
+ ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+ crc = imx_calcethcrc(mac, 6);
+ hashindex = (crc >> 26) & 0x3f;
+
+ return hashindex;
+}
+#endif
+
+/****************************************************************************
+ * Function: imx_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int imx_addmac(struct net_driver_s *dev, FAR const uint8_t *mac)
+{
+ uint32_t hashindex;
+ uint32_t temp;
+ uint32_t registeraddress;
+
+ hashindex = imx_enet_hash_index(mac);
+
+ /* Add the MAC address to the hardware multicast routing table */
+
+ if (hashindex > 31)
+ {
+ registeraddress = IMX_ENET_GAUR;
+ hashindex -= 32;
+ }
+ else
+ {
+ registeraddress = IMX_ENET_GALR;
+ }
+
+ temp = getreg32(registeraddress);
+ temp |= 1 << hashindex;
+ putreg32(temp, registeraddress);
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: imx_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware
+ * multicast address filtering
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int imx_rmmac(struct net_driver_s *dev, FAR const uint8_t *mac)
+{
+ uint32_t hashindex;
+ uint32_t temp;
+ uint32_t registeraddress;
+
+ /* Remove the MAC address from the hardware multicast routing table */
+
+ hashindex = imx_enet_hash_index(mac);
+
+ if (hashindex > 31)
+ {
+ registeraddress = IMX_ENET_GAUR;
+ hashindex -= 32;
+ }
+ else
+ {
+ registeraddress = IMX_ENET_GALR;
+ }
+
+ temp = getreg32(registeraddress);
+ temp &= ~(1 << hashindex);
+ putreg32(temp, registeraddress);
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: imx_ioctl
+ *
+ * Description:
+ * PHY ioctl command handler
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * cmd - ioctl command
+ * arg - Argument accompanying the command
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int imx_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+ FAR struct imx_driver_s *priv =
+ (FAR struct imx_driver_s *)dev->d_private;
+#endif
+ int ret;
+
+ switch (cmd)
+ {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+ case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+ {
+ struct mii_ioctl_notify_s *req =
+ (struct mii_ioctl_notify_s *)((uintptr_t)arg);
+
+ ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
+ if (ret == OK)
+ {
+ /* Enable PHY link up/down interrupts */
+
+ ret = imx_phyintenable(priv);
+ }
+ }
+ break;
+#endif
+
+ case SIOCGMIIPHY: /* Get MII PHY address */
+ {
+ struct mii_ioctl_data_s *req =
+ (struct mii_ioctl_data_s *)((uintptr_t)arg);
+ req->phy_id = priv->phyaddr;
+ ret = OK;
+ }
+ break;
+
+ case SIOCGMIIREG: /* Get register from MII PHY */
+ {
+ struct mii_ioctl_data_s *req =
+ (struct mii_ioctl_data_s *)((uintptr_t)arg);
+ ret = imx_readmii(priv, req->phy_id, req->reg_num, &req->val_out);
+ }
+ break;
+
+ case SIOCSMIIREG: /* Set register in MII PHY */
+ {
+ struct mii_ioctl_data_s *req =
+ (struct mii_ioctl_data_s *)((uintptr_t)arg);
+ ret = imx_writemii(priv, req->phy_id, req->reg_num, req->val_in);
+ }
+ break;
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: imx_phyintenable
+ *
+ * Description:
+ * Enable link up/down PHY interrupts. The interrupt protocol is like this:
+ *
+ * - Interrupt status is cleared when the interrupt is enabled.
+ * - Interrupt occurs. Interrupt is disabled (at the processor level) when
+ * is received.
+ * - Interrupt status is cleared when the interrupt is re-enabled.
+ *
+ * Input Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT)
+static int imx_phyintenable(struct imx_driver_s *priv)
+{
+#if defined(CONFIG_ETH0_PHY_KSZ8051) || defined(CONFIG_ETH0_PHY_KSZ8061) || \
+ defined(CONFIG_ETH0_PHY_KSZ8081)
+ uint16_t phyval;
+ int ret;
+
+ /* Read the interrupt status register in order to clear any pending
+ * interrupts
+ */
+
+ ret = imx_readmii(priv, priv->phyaddr, MII_KSZ8081_INT, &phyval);
+ if (ret == OK)
+ {
+ /* Enable link up/down interrupts */
+
+ ret = imx_writemii(priv, priv->phyaddr, MII_KSZ8081_INT,
+ (MII_KSZ80X1_INT_LDEN | MII_KSZ80X1_INT_LUEN));
+ }
+
+ return ret;
+#else
+# error Unrecognized PHY
+ return -ENOSYS;
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Function: imx_initmii
+ *
+ * Description:
+ * Configure the MII interface
+ *
+ * Input Parameters:
+ * priv - Reference to the private ENET driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void imx_initmii(struct imx_driver_s *priv)
+{
+ /* Speed is based on the peripheral (bus) clock; hold time is 2 module
+ * clock. This hold time value may need to be increased on some platforms
+ */
+
+ putreg32(ENET_MSCR_HOLDTIME_2CYCLES |
+ IMX_MII_SPEED << ENET_MSCR_MII_SPEED_SHIFT,
+ IMX_ENET_MSCR);
+}
+
+/****************************************************************************
+ * Function: imx_writemii
+ *
+ * Description:
+ * Write a 16-bit value to a PHY register.
+ *
+ * Input Parameters:
+ * priv - Reference to the private ENET driver state structure
+ * phyaddr - The PHY address
+ * regaddr - The PHY register address
+ * data - The data to write to the PHY register
+ *
+ * Returned Value:
+ * Zero on success, a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#if 0 /* TODO */
+static int imx_writemii(struct imx_driver_s *priv, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t data)
+{
+ int timeout;
+
+ /* Clear the MII interrupt bit */
+
+ putreg32(ENET_INT_MII, IMX_ENET_EIR);
+
+ /* Initiate the MII Management write */
+
+ putreg32(data |
+ 2 << ENET_MMFR_TA_SHIFT |
+ (uint32_t)regaddr << ENET_MMFR_RA_SHIFT |
+ (uint32_t)phyaddr << ENET_MMFR_PA_SHIFT |
+ ENET_MMFR_OP_WRMII |
+ 1 << ENET_MMFR_ST_SHIFT,
+ IMX_ENET_MMFR);
+
+ /* Wait for the transfer to complete */
+
+ for (timeout = 0; timeout < MII_MAXPOLLS; timeout++)
+ {
+ if ((getreg32(IMX_ENET_EIR) & ENET_INT_MII) != 0)
+ {
+ break;
+ }
+ }
+
+ /* Check for a timeout */
+
+ if (timeout == MII_MAXPOLLS)
+ {
+ return -ETIMEDOUT;
+ }
+
+ /* Clear the MII interrupt bit */
+
+ putreg32(ENET_INT_MII, IMX_ENET_EIR);
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: imx_reademii
+ *
+ * Description:
+ * Read a 16-bit value from a PHY register.
+ *
+ * Input Parameters:
+ * priv - Reference to the private ENET driver state structure
+ * phyaddr - The PHY address
+ * regaddr - The PHY register address
+ * data - A pointer to the location to return the data
+ *
+ * Returned Value:
+ * Zero on success, a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#if 0 /* TODO */
+static int imx_readmii(struct imx_driver_s *priv, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t *data)
+{
+ int timeout;
+
+ /* Clear the MII interrupt bit */
+
+ putreg32(ENET_INT_MII, IMX_ENET_EIR);
+
+ /* Initiate the MII Management read */
+
+ putreg32(2 << ENET_MMFR_TA_SHIFT |
+ (uint32_t)regaddr << ENET_MMFR_RA_SHIFT |
+ (uint32_t)phyaddr << ENET_MMFR_PA_SHIFT |
+ ENET_MMFR_OP_RDMII |
+ 1 << ENET_MMFR_ST_SHIFT,
+ IMX_ENET_MMFR);
+
+ /* Wait for the transfer to complete */
+
+ for (timeout = 0; timeout < MII_MAXPOLLS; timeout++)
+ {
+ if ((getreg32(IMX_ENET_EIR) & ENET_INT_MII) != 0)
+ {
+ break;
+ }
+ }
+
+ /* Check for a timeout */
+
+ if (timeout >= MII_MAXPOLLS)
+ {
+ nerr("ERROR: Timed out waiting for transfer to complete\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Clear the MII interrupt bit */
+
+ putreg32(ENET_INT_MII, IMX_ENET_EIR);
+
+ /* And return the MII data */
+
+ *data = (uint16_t)(getreg32(IMX_ENET_MMFR) & ENET_MMFR_DATA_MASK);
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: imx_initphy
+ *
+ * Description:
+ * Configure the PHY
+ *
+ * Input Parameters:
+ * priv - Reference to the private ENET driver state structure
+ * renogphy - Flag indicating if to perform negotiation of the link
+ *
+ * Returned Value:
+ * Zero (OK) returned on success; a negated errno value is returned on any
+ * failure;
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline int imx_initphy(struct imx_driver_s *priv, bool renogphy)
+{
+#if 0 /* TODO */
+ uint32_t rcr;
+ uint32_t tcr;
+ uint32_t racc;
+ uint16_t phydata;
+ uint8_t phyaddr = BOARD_PHY_ADDR;
+ int retries;
+ int ret;
+
+ if (renogphy)
+ {
+ /* Loop (potentially infinitely?) until we successfully communicate
+ * with the PHY. This is 'standard stuff' that should work for any PHY
+ * - we are not communicating with it's 'special' registers
+ * at this point.
+ */
+
+ ninfo("%s: Try phyaddr: %u\n", BOARD_PHY_NAME, phyaddr);
+
+ /* Try to read PHYID1 few times using this address */
+
+ retries = 0;
+ do
+ {
+ nxsig_usleep(LINK_WAITUS);
+
+ ninfo("%s: Read PHYID1, retries=%d\n",
+ BOARD_PHY_NAME, retries + 1);
+
+ phydata = 0xffff;
+ ret = imx_readmii(priv, phyaddr, MII_PHYID1, &phydata);
+ }
+ while ((ret < 0 || phydata == 0xffff) && ++retries < 3);
+
+ if (retries >= 3)
+ {
+ nerr("ERROR: Failed to read %s PHYID1 at address %d\n",
+ BOARD_PHY_NAME, phyaddr);
+ return -ENOENT;
+ }
+
+ ninfo("%s: Using PHY address %u\n", BOARD_PHY_NAME, phyaddr);
+ priv->phyaddr = phyaddr;
+
+ /* Verify PHYID1. Compare OUI bits 3-18 */
+
+ ninfo("%s: PHYID1: %04x\n", BOARD_PHY_NAME, phydata);
+ if (phydata != BOARD_PHYID1)
+ {
+ nerr("ERROR: PHYID1=%04x incorrect for %s. Expected %04x\n",
+ phydata, BOARD_PHY_NAME, BOARD_PHYID1);
+ return -ENXIO;
+ }
+
+ /* Read PHYID2 */
+
+ ret = imx_readmii(priv, phyaddr, MII_PHYID2, &phydata);
+ if (ret < 0)
+ {
+ nerr("ERROR: Failed to read %s PHYID2: %d\n", BOARD_PHY_NAME, ret);
+ return ret;
+ }
+
+ ninfo("%s: PHYID2: %04x\n", BOARD_PHY_NAME, phydata);
+
+ /* Verify PHYID2: Compare OUI bits 19-24 and the 6-bit model number
+ * (ignoring the 4-bit revision number).
+ */
+
+ if ((phydata & 0xfff0) != (BOARD_PHYID2 & 0xfff0))
+ {
+ nerr("ERROR: PHYID2=%04x incorrect for %s. Expected %04x\n",
+ (phydata & 0xfff0), BOARD_PHY_NAME, (BOARD_PHYID2 & 0xfff0));
+ return -ENXIO;
+ }
+
+#ifdef CONFIG_ETH0_PHY_KSZ8081
+ /* Reset PHY */
+
+ imx_writemii(priv, phyaddr, MII_MCR, MII_MCR_RESET);
+
+ /* Set RMII mode */
+
+ ret = imx_readmii(priv, phyaddr, MII_KSZ8081_PHYCTRL2, &phydata);
+ if (ret < 0)
+ {
+ nerr("ERROR: Failed to read MII_KSZ8081_PHYCTRL2\n");
+ return ret;
+ }
+
+ /* Indicate 50MHz clock */
+
+ imx_writemii(priv, phyaddr, MII_KSZ8081_PHYCTRL2,
+ (phydata | (1 << 7)));
+
+ /* Switch off NAND Tree mode (in case it was set via pinning) */
+
+ ret = imx_readmii(priv, phyaddr, MII_KSZ8081_OMSO, &phydata);
+ if (ret < 0)
+ {
+ nerr("ERROR: Failed to read MII_KSZ8081_OMSO: %d\n", ret);
+ return ret;
+ }
+
+ imx_writemii(priv, phyaddr, MII_KSZ8081_OMSO,
+ (phydata & ~(1 << 5)));
+
+ /* Set Ethernet led to green = activity and yellow = link and */
+
+ ret = imx_readmii(priv, phyaddr, MII_KSZ8081_PHYCTRL2, &phydata);
+ if (ret < 0)
+ {
+ nerr("ERROR: Failed to read MII_KSZ8081_PHYCTRL2\n");
+ return ret;
+ }
+
+ imx_writemii(priv, phyaddr, MII_KSZ8081_PHYCTRL2,
+ (phydata | (1 << 4)));
+
+#elif defined (CONFIG_ETH0_PHY_LAN8720)
+ /* Make sure that PHY comes up in correct mode when it's reset */
+
+ imx_writemii(priv, phyaddr, MII_LAN8720_MODES,
+ MII_LAN8720_MODES_RESV | MII_LAN8720_MODES_ALL |
+ MII_LAN8720_MODES_PHYAD(BOARD_PHY_ADDR));
+
+ /* ...and reset PHY */
+
+ imx_writemii(priv, phyaddr, MII_MCR, MII_MCR_RESET);
+#endif
+
+ /* Start auto negotiation */
+
+ ninfo("%s: Start Autonegotiation...\n", BOARD_PHY_NAME);
+ imx_writemii(priv, phyaddr, MII_MCR,
+ (MII_MCR_ANRESTART | MII_MCR_ANENABLE));
+
+ /* Wait for auto negotiation to complete */
+
+ for (retries = 0; retries < LINK_NLOOPS; retries++)
+ {
+ ret = imx_readmii(priv, phyaddr, MII_MSR, &phydata);
+ if (ret < 0)
+ {
+ nerr("ERROR: Failed to read %s MII_MSR: %d\n",
+ BOARD_PHY_NAME, ret);
+ return ret;
+ }
+
+ if (phydata & MII_MSR_ANEGCOMPLETE)
+ {
+ break;
+ }
+
+ nxsig_usleep(LINK_WAITUS);
+ }
+
+ if (phydata & MII_MSR_ANEGCOMPLETE)
+ {
+ ninfo("%s: Autonegotiation complete\n", BOARD_PHY_NAME);
+ ninfo("%s: MII_MSR: %04x\n", BOARD_PHY_NAME, phydata);
+ }
+ else
+ {
+ /* TODO: Autonegotiation has right now failed. Maybe the Eth cable
+ * is not connected. PHY chip have mechanisms to configure link
+ * OK. We should leave autconf on, and find a way to re-configure
+ * MCU whenever the link is ready.
+ */
+
+ ninfo("%s: Autonegotiation failed [%d] (is cable plugged-in ?), "
+ "default to 10Mbs mode\n", \
+ BOARD_PHY_NAME, retries);
+
+ /* Stop auto negotiation */
+
+ imx_writemii(priv, phyaddr, MII_MCR, 0);
+ }
+ }
+
+ /* When we get here we have a (negotiated) speed and duplex. This is also
+ * the point we enter if renegotiation is turned off, so have multiple
+ * attempts at reading the status register in case the PHY isn't awake
+ * properly.
+ */
+
+ retries = 0;
+ do
+ {
+ phydata = 0xffff;
+ ret = imx_readmii(priv, phyaddr, BOARD_PHY_STATUS, &phydata);
+ }
+ while ((ret < 0 || phydata == 0xffff) && ++retries < 3);
+
+ /* If we didn't successfully read anything and we haven't tried a physical
+ * renegotiation then lets do that
+ */
+
+ if (retries >= 3)
+ {
+ if (renogphy == false)
+ {
+ /* Give things one more chance with renegotiation turned on */
+
+ return imx_initphy(priv, true);
+ }
+ else
+ {
+ /* That didn't end well, just give up */
+
+ nerr("ERROR: Failed to read %s BOARD_PHY_STATUS[%02x]: %d\n",
+ BOARD_PHY_NAME, BOARD_PHY_STATUS, ret);
+ return ret;
+ }
+ }
+
+ ninfo("%s: BOARD_PHY_STATUS: %04x\n", BOARD_PHY_NAME, phydata);
+
+ /* Set up the transmit and receive control registers based on the
+ * configuration and the auto negotiation results.
+ */
+
+#ifdef CONFIG_IMX_ENETUSEMII
+ rcr = ENET_RCR_CRCFWD |
+ CONFIG_NET_ETH_PKTSIZE << ENET_RCR_MAX_FL_SHIFT |
+ ENET_RCR_MII_MODE;
+#else
+ rcr = ENET_RCR_RMII_MODE | ENET_RCR_CRCFWD |
+ CONFIG_NET_ETH_PKTSIZE << ENET_RCR_MAX_FL_SHIFT |
+ ENET_RCR_MII_MODE;
+#endif
+ tcr = 0;
+
+ putreg32(rcr, IMX_ENET_RCR);
+ putreg32(tcr, IMX_ENET_TCR);
+
+ /* Enable Discard Of Frames With MAC Layer Errors.
+ * Enable Discard Of Frames With Wrong Protocol Checksum.
+ * Bit 1: Enable discard of frames with wrong IPv4 header checksum.
+ */
+
+ racc = ENET_RACC_PRODIS | ENET_RACC_LINEDIS | ENET_RACC_IPDIS;
+ putreg32(racc, IMX_ENET_RACC);
+
+ /* Setup half or full duplex */
+
+ if (BOARD_PHY_ISDUPLEX(phydata))
+ {
+ /* Full duplex */
+
+ ninfo("%s: Full duplex\n", BOARD_PHY_NAME);
+ tcr |= ENET_TCR_FDEN;
+ }
+ else
+ {
+ /* Half duplex */
+
+ ninfo("%s: Half duplex\n", BOARD_PHY_NAME);
+ rcr |= ENET_RCR_DRT;
+ }
+
+ if (BOARD_PHY_10BASET(phydata))
+ {
+ /* 10 Mbps */
+
+ ninfo("%s: 10 Base-T\n", BOARD_PHY_NAME);
+ rcr |= ENET_RCR_RMII_10T;
+ }
+ else if (BOARD_PHY_100BASET(phydata))
+ {
+ /* 100 Mbps */
+
+ ninfo("%s: 100 Base-T\n", BOARD_PHY_NAME);
+ }
+ else
+ {
+ /* This might happen if Autonegotiation did not complete(?) */
+
+ nerr("ERROR: Neither 10- nor 100-BaseT reported: PHY STATUS=%04x\n",
+ phydata);
+ return -EIO;
+ }
+
+ putreg32(rcr, IMX_ENET_RCR);
+ putreg32(tcr, IMX_ENET_TCR);
+#endif
+ return OK;
+}
+
+/****************************************************************************
+ * Function: imx_initbuffers
+ *
+ * Description:
+ * Initialize ENET buffers and descriptors
+ *
+ * Input Parameters:
+ * priv - Reference to the private ENET driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void imx_initbuffers(struct imx_driver_s *priv)
+{
+ uintptr_t addr;
+ int i;
+
+ /* Get an aligned TX descriptor (array) address */
+
+ addr = (uintptr_t)g_desc_pool;
+ priv->txdesc = (struct enet_desc_s *)addr;
+
+ /* Get an aligned RX descriptor (array) address */
+
+ addr += CONFIG_IMX_ENET_NTXBUFFERS * sizeof(struct enet_desc_s);
+ priv->rxdesc = (struct enet_desc_s *)addr;
+
+ /* Get the beginning of the first aligned buffer */
+
+ addr = (uintptr_t)g_buffer_pool;
+
+ /* Then fill in the TX descriptors */
+
+ for (i = 0; i < CONFIG_IMX_ENET_NTXBUFFERS; i++)
+ {
+ priv->txdesc[i].status1 = 0;
+ priv->txdesc[i].length = 0;
+ priv->txdesc[i].data = (uint8_t *)imx_swap32((uint32_t)addr);
+#ifdef CONFIG_IMX_ENETENHANCEDBD
+ priv->txdesc[i].status2 = TXDESC_IINS | TXDESC_PINS;
+#endif
+ addr += IMX_BUF_SIZE;
+ }
+
+ /* Then fill in the RX descriptors */
+
+ for (i = 0; i < CONFIG_IMX_ENET_NRXBUFFERS; i++)
+ {
+ priv->rxdesc[i].status1 = RXDESC_E;
+ priv->rxdesc[i].length = 0;
+ priv->rxdesc[i].data = (uint8_t *)imx_swap32((uint32_t)addr);
+#ifdef CONFIG_IMX_ENETENHANCEDBD
+ priv->rxdesc[i].bdu = 0;
+ priv->rxdesc[i].status2 = RXDESC_INT;
+#endif
+ addr += IMX_BUF_SIZE;
+ }
+
+ /* Set the wrap bit in the last descriptors to form a ring */
+
+ priv->txdesc[CONFIG_IMX_ENET_NTXBUFFERS - 1].status1 |= TXDESC_W;
+ priv->rxdesc[CONFIG_IMX_ENET_NRXBUFFERS - 1].status1 |= RXDESC_W;
+
+ /* We start with RX descriptor 0 and with no TX descriptors in use */
+
+ priv->txtail = 0;
+ priv->txhead = 0;
+ priv->rxtail = 0;
+
+ /* Initialize the packet buffer, which is used when sending */
+
+ priv->dev.d_buf =
+ (uint8_t *)imx_swap32((uint32_t)priv->txdesc[priv->txhead].data);
+}
+
+/****************************************************************************
+ * Function: imx_reset
+ *
+ * Description:
+ * Put the EMAC in the non-operational, reset state
+ *
+ * Input Parameters:
+ * priv - Reference to the private ENET driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void imx_reset(struct imx_driver_s *priv)
+{
+ unsigned int i;
+
+ /* Set the reset bit and clear the enable bit */
+
+ putreg32(ENET_ECR_RESET, IMX_ENET_ECR);
+
+ /* Wait at least 8 clock cycles */
+
+ for (i = 0; i < 10; i++)
+ {
+ asm volatile ("nop");
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: imx_netinitialize
+ *
+ * Description:
+ * Initialize the Ethernet controller and driver
+ *
+ * Input Parameters:
+ * intf - In the case where there are multiple EMACs, this value
+ * identifies which EMAC is to be initialized.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+int imx_netinitialize(int intf)
+{
+ struct imx_driver_s *priv;
+#ifdef CONFIG_NET_ETHERNET
+ uint32_t uidl = 0;
+ uint32_t uidml = 0;
+ uint8_t *mac;
+#endif
+ int ret;
+
+ /* Get the interface structure associated with this interface number. */
+
+ DEBUGASSERT(intf < CONFIG_IMX_ENET_NETHIFS);
+ priv = &g_enet[intf];
+
+#if 0 /* TODO */
+ uint32_t regval;
+
+ /* Enable ENET1_TX_CLK_DIR (Provides 50MHz clk OUT to PHY) */
+
+ regval = getreg32(IMX_IOMUXC_GPR_GPR1);
+ regval |= GPR_GPR1_ENET1_TX_CLK_OUT_EN;
+ putreg32(regval, IMX_IOMUXC_GPR_GPR1);
+
+ /* Enable the ENET clock. Clock is on during all modes,
+ * except STOP mode.
+ */
+
+ imx_clockall_enet();
+
+ /* Configure all ENET/MII pins */
+
+ imx_config_gpio(GPIO_ENET_MDIO);
+ imx_config_gpio(GPIO_ENET_MDC);
+ imx_config_gpio(GPIO_ENET_RX_EN);
+ imx_config_gpio(GPIO_ENET_RX_DATA00);
+ imx_config_gpio(GPIO_ENET_RX_DATA01);
+ imx_config_gpio(GPIO_ENET_TX_DATA00);
+ imx_config_gpio(GPIO_ENET_TX_DATA01);
+ imx_config_gpio(GPIO_ENET_TX_CLK);
+ imx_config_gpio(GPIO_ENET_TX_EN);
+#ifdef GPIO_ENET_RX_ER
+ imx_config_gpio(GPIO_ENET_RX_ER);
+#endif
+
+#endif /* TODO */
+
+ /* Attach the Ethernet MAC IEEE 1588 timer interrupt handler */
+
+#if 0
+ if (irq_attach(IMX_IRQ_ENET0TMR, imx_tmrinterrupt, NULL))
+ {
+ /* We could not attach the ISR to the interrupt */
+
+ nerr("ERROR: Failed to attach ENET0TMR IRQ\n");
+ return -EAGAIN;
+ }
+#endif
+
+ /* Attach the Ethernet interrupt handler */
+
+ if (irq_attach(IMX_IRQ_ENET0, imx_enet_interrupt, NULL))
+ {
+ /* We could not attach the ISR to the interrupt */
+
+ nerr("ERROR: Failed to attach ENET0 IRQ\n");
+ return -EAGAIN;
+ }
+
+ /* Configure as a (high) level interrupt */
+
+ arm_gic_irq_trigger(IMX_IRQ_ENET0, false);
+
+ /* Initialize the driver structure */
+
+ memset(priv, 0, sizeof(struct imx_driver_s));
+ priv->dev.d_ifup = imx_ifup; /* I/F up (new IP address) callback */
+ priv->dev.d_ifdown = imx_ifdown; /* I/F down callback */
+ priv->dev.d_txavail = imx_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_MCASTGROUP
+ priv->dev.d_addmac = imx_addmac; /* Add multicast MAC address */
+ priv->dev.d_rmmac = imx_rmmac; /* Remove multicast MAC address */
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+ priv->dev.d_ioctl = imx_ioctl; /* Support PHY ioctl() calls */
+#endif
+ priv->dev.d_private = g_enet; /* Used to recover private state from dev */
+
+#ifdef CONFIG_NET_ETHERNET
+ /* Determine a semi-unique MAC address from MCU UID
+ * We use UID Low and Mid Low registers to get 64 bits, from which we keep
+ * 48 bits. We then force unicast and locally administered bits
+ * (b0 and b1, 1st octet)
+ */
+
+ /* hardcoded offset: todo: need proper header file */
+
+#if 0 /* TODO */
+ uidl = getreg32(IMX_OCOTP_BASE + 0x410);
+ uidml = getreg32(IMX_OCOTP_BASE + 0x420);
+#endif
+
+ mac = priv->dev.d_mac.ether.ether_addr_octet;
+
+ uidml |= 0x00000200;
+ uidml &= 0x0000feff;
+
+ mac[0] = (uidml & 0x0000ff00) >> 8;
+ mac[1] = (uidml & 0x000000ff);
+ mac[2] = (uidl & 0xff000000) >> 24;
+ mac[3] = (uidl & 0x00ff0000) >> 16;
+ mac[4] = (uidl & 0x0000ff00) >> 8;
+ mac[5] = (uidl & 0x000000ff);
+#endif
+
+#ifdef CONFIG_IMX_ENET_PHYINIT
+ /* Perform any necessary, one-time, board-specific PHY initialization */
+
+ ret = imx_phy_boardinitialize(0);
+ if (ret < 0)
+ {
+ nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
+ return ret;
+ }
+#endif
+
+ /* Put the interface in the down state. This usually amounts to resetting
+ * the device and/or calling imx_ifdown().
+ */
+
+ imx_ifdown(&priv->dev);
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ netdev_register(&priv->dev, NET_LL_ETHERNET);
+
+ UNUSED(ret);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: up_netinitialize
+ *
+ * Description:
+ * Initialize the first network interface. If there are more than one
+ * interface in the chip, then board-specific logic will have to provide
+ * this function to determine which, if any, Ethernet controllers should
+ * be initialized.
+ *
+ ****************************************************************************/
+
+#if CONFIG_IMX_ENET_NETHIFS == 1 && !defined(CONFIG_NETDEV_LATEINIT)
+void arm_netinitialize(void)
+{
+ imx_netinitialize(0);
+}
+#endif
+
+#endif /* CONFIG_IMX_ENET */
diff --git a/arch/arm/src/imx6/imx_enet.h b/arch/arm/src/imx6/imx_enet.h
new file mode 100644
index 0000000..b8e59e0
--- /dev/null
+++ b/arch/arm/src/imx6/imx_enet.h
@@ -0,0 +1,108 @@
+/************************************************************************************
+ * arch/arm/src/imx6/imx_enet.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ************************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_IMX6_IMX_ENET_H
+#define __ARCH_ARM_SRC_IMX6_IMX_ENET_H
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "hardware/imx_enet.h"
+
+#ifdef CONFIG_IMX_ENET
+
+/************************************************************************************
+ * Pre-processor Definitions
+ ************************************************************************************/
+
+/* Definitions for use with imx_phy_boardinitialize */
+
+#define EMAC_INTF 0
+
+/************************************************************************************
+ * Public Function Prototypes
+ ************************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/************************************************************************************
+ * Function: up_netinitialize
+ *
+ * Description:
+ * Initialize the first network interface. If there are more than one
+ * interface in the chip, then board-specific logic will have to provide
+ * this function to determine which, if any, Ethernet controllers should
+ * be initialized. Also prototyped in up_internal.h.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ * Called very early in the initialization sequence.
+ *
+ ************************************************************************************/
+
+void up_netinitialize(void);
+
+/************************************************************************************
+ * Function: imx_phy_boardinitialize
+ *
+ * Description:
+ * Some boards require specialized initialization of the PHY before it can be
+ * used. This may include such things as configuring GPIOs, resetting the PHY,
+ * etc. If CONFIG_IMX_ENET_PHYINIT is defined in the configuration then the
+ * board specific logic must provide imx_phyinitialize(); The i.MX RT Ethernet
+ * driver will call this function one time before it first uses the PHY.
+ *
+ * Input Parameters:
+ * intf - Always zero for now.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ ************************************************************************************/
+
+#ifdef CONFIG_IMX_ENET_PHYINIT
+int imx_phy_boardinitialize(int intf);
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* CONFIG_IMX_ENET */
+#endif /* __ARCH_ARM_SRC_IMX6_IMX_ENET_H */
diff --git a/boards/arm/imx6/sabre-6quad/configs/netnsh/defconfig b/boards/arm/imx6/sabre-6quad/configs/netnsh/defconfig
new file mode 100644
index 0000000..b53d32a
--- /dev/null
+++ b/boards/arm/imx6/sabre-6quad/configs/netnsh/defconfig
@@ -0,0 +1,88 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="sabre-6quad"
+CONFIG_ARCH_BOARD_SABRE_6QUAD=y
+CONFIG_ARCH_BUTTONS=y
+CONFIG_ARCH_CHIP="imx6"
+CONFIG_ARCH_CHIP_IMX6=y
+CONFIG_ARCH_CHIP_IMX6_6QUAD=y
+CONFIG_ARCH_INTERRUPTSTACK=2048
+CONFIG_ARCH_IRQBUTTONS=y
+CONFIG_ARCH_LOWVECTORS=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BOARD_LOOPSPERMSEC=99369
+CONFIG_BOOT_RUNFROMSDRAM=y
+CONFIG_BUILTIN=y
+CONFIG_CLOCK_MONOTONIC=y
+CONFIG_DEBUG_ASSERTIONS=y
+CONFIG_DEBUG_ERROR=y
+CONFIG_DEBUG_FEATURES=y
+CONFIG_DEBUG_FULLOPT=y
+CONFIG_DEBUG_NET=y
+CONFIG_DEBUG_NET_ERROR=y
+CONFIG_DEBUG_NET_WARN=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_DEBUG_WARN=y
+CONFIG_DEV_ZERO=y
+CONFIG_ETH0_PHY_KSZ8081=y
+CONFIG_EXAMPLES_HELLO=y
+CONFIG_FS_PROCFS=y
+CONFIG_HAVE_CXX=y
+CONFIG_HAVE_CXXINITIALIZE=y
+CONFIG_IMX6_ENET=y
+CONFIG_IMX6_UART1=y
+CONFIG_IMX_DDR_SIZE=1073741824
+CONFIG_INTELHEX_BINARY=y
+CONFIG_MAX_TASKS=16
+CONFIG_NET=y
+CONFIG_NETDB_DNSCLIENT=y
+CONFIG_NETDB_DNSCLIENT_ENTRIES=4
+CONFIG_NETDB_DNSSERVER_NOADDR=y
+CONFIG_NETINIT_DRIPADDR=0x0a000201
+CONFIG_NETINIT_IPADDR=0x0a00020f
+CONFIG_NETINIT_NOMAC=y
+CONFIG_NETUTILS_DHCPC=y
+CONFIG_NETUTILS_TELNETD=y
+CONFIG_NETUTILS_TFTPC=y
+CONFIG_NETUTILS_WEBCLIENT=y
+CONFIG_NET_BROADCAST=y
+CONFIG_NET_ETH_PKTSIZE=1500
+CONFIG_NET_ICMP=y
+CONFIG_NET_ICMP_SOCKET=y
+CONFIG_NET_MAX_LISTENPORTS=8
+CONFIG_NET_STATISTICS=y
+CONFIG_NET_TCP=y
+CONFIG_NET_UDP=y
+CONFIG_NET_UDP_CHECKSUMS=y
+CONFIG_NFILE_DESCRIPTORS=8
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_READLINE=y
+CONFIG_PREALLOC_TIMERS=4
+CONFIG_RAM_SIZE=1073741824
+CONFIG_RAM_START=0x10000000
+CONFIG_RAM_VSTART=0x10000000
+CONFIG_RAW_BINARY=y
+CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_HPWORK=y
+CONFIG_SCHED_HPWORKPRIORITY=192
+CONFIG_SCHED_WAITPID=y
+CONFIG_STACK_COLORATION=y
+CONFIG_START_DAY=23
+CONFIG_START_MONTH=12
+CONFIG_START_YEAR=2020
+CONFIG_SYMTAB_ORDEREDBYNAME=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_TESTING_GETPRIME=y
+CONFIG_TESTING_OSTEST=y
+CONFIG_UART1_SERIAL_CONSOLE=y
+CONFIG_USER_ENTRYPOINT="nsh_main"