summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2015-03-16 13:51:37 -0600
committerGregory Nutt <gnutt@nuttx.org>2015-03-16 13:51:37 -0600
commite0ba93b5aa36cc418979ba794297633efe131dc5 (patch)
tree3c06d84206254fd984aa50185a6a08c8c4247470
parent501552171e75e2325a923c2d8526e05cf87ac162 (diff)
downloadpx4-nuttx-e0ba93b5aa36cc418979ba794297633efe131dc5.tar.gz
px4-nuttx-e0ba93b5aa36cc418979ba794297633efe131dc5.tar.bz2
px4-nuttx-e0ba93b5aa36cc418979ba794297633efe131dc5.zip
SAMV7: Quick'n'dirty port of the SAMA5D4 Ethernet MAC driver to the SAMV7. Still some unresovled issues with DCache handling
-rw-r--r--nuttx/arch/arm/include/samv7/samv71_irq.h4
-rw-r--r--nuttx/arch/arm/src/samv7/Kconfig229
-rw-r--r--nuttx/arch/arm/src/samv7/Make.defs4
-rw-r--r--nuttx/arch/arm/src/samv7/chip/samv71_pinmap.h44
-rw-r--r--nuttx/arch/arm/src/samv7/sam_emac.c4656
-rw-r--r--nuttx/arch/arm/src/samv7/sam_ethernet.c118
-rw-r--r--nuttx/arch/arm/src/samv7/sam_ethernet.h234
-rw-r--r--nuttx/arch/arm/src/samv7/samv71_periphclks.h4
-rw-r--r--nuttx/configs/samv71-xult/README.txt243
-rw-r--r--nuttx/configs/samv71-xult/src/sam_ethernet.c272
-rw-r--r--nuttx/configs/samv71-xult/src/samv71-xult.h27
-rw-r--r--nuttx/drivers/net/Kconfig3
12 files changed, 5810 insertions, 28 deletions
diff --git a/nuttx/arch/arm/include/samv7/samv71_irq.h b/nuttx/arch/arm/include/samv7/samv71_irq.h
index b757d7068..221985679 100644
--- a/nuttx/arch/arm/include/samv7/samv71_irq.h
+++ b/nuttx/arch/arm/include/samv7/samv71_irq.h
@@ -89,7 +89,7 @@
#define SAM_PID_MCAN01 (36) /* CAN0 IRQ line 1 */
#define SAM_PID_MCAN10 (37) /* CAN1 IRQ line 0 */
#define SAM_PID_MCAN11 (38) /* CAN1 IRQ line 1 */
-#define SAM_PID_EMAC (39) /* Ethernet MAC */
+#define SAM_PID_EMAC0 (39) /* Ethernet MAC */
#define SAM_PID_AFEC1 (40) /* Analog Front End 1 */
#define SAM_PID_TWIHS2 (41) /* Two-Wire Interface 2 */
#define SAM_PID_SPI1 (42) /* Serial Peripheral Interface 1 */
@@ -159,7 +159,7 @@
#define SAM_IRQ_MCAN01 (SAM_IRQ_EXTINT+SAM_PID_MCAN01) /* CAN0 IRQ line 1 */
#define SAM_IRQ_MCAN10 (SAM_IRQ_EXTINT+SAM_PID_MCAN10) /* CAN1 IRQ line 0 */
#define SAM_IRQ_MCAN11 (SAM_IRQ_EXTINT+SAM_PID_MCAN11) /* CAN1 IRQ line 1 */
-#define SAM_IRQ_EMAC (SAM_IRQ_EXTINT+SAM_PID_EMAC) /* Ethernet MAC */
+#define SAM_IRQ_EMAC0 (SAM_IRQ_EXTINT+SAM_PID_EMAC0) /* Ethernet MAC */
#define SAM_IRQ_AFEC1 (SAM_IRQ_EXTINT+SAM_PID_AFEC1) /* Analog Front End 1 */
#define SAM_IRQ_TWIHS2 (SAM_IRQ_EXTINT+SAM_PID_TWIHS2) /* Two-Wire Interface 2 */
#define SAM_IRQ_SPI1 (SAM_IRQ_EXTINT+SAM_PID_SPI1) /* Serial Peripheral Interface 1 */
diff --git a/nuttx/arch/arm/src/samv7/Kconfig b/nuttx/arch/arm/src/samv7/Kconfig
index 298e1e9f1..074f0d115 100644
--- a/nuttx/arch/arm/src/samv7/Kconfig
+++ b/nuttx/arch/arm/src/samv7/Kconfig
@@ -114,6 +114,10 @@ config SAMV7_HAVE_EBI
bool
default n
+config SAMV7_EMAC
+ bool
+ default n
+
config SAMV7_HSMCI
bool
default n
@@ -217,9 +221,10 @@ config SAMV7_EBI
default n
depends on SAMV7_HAVE_EBI
-config SAMV7_EMAC
+config SAMV7_EMAC0
bool "Ethernet MAC (GMAC)"
default n
+ select SAMV7_EMAC
select NETDEVICES
select ARCH_HAVE_PHY
@@ -1009,3 +1014,225 @@ config SAMV7_HSMCI_REGDEBUG
Very invasive! Requires also DEBUG.
endmenu # HSMCI device driver options
+
+menu "EMAC device driver options"
+ depends on SAMV7_EMAC0
+
+config SAMV7_EMAC0_NRXBUFFERS
+ int "Number of RX buffers"
+ default 16
+ ---help---
+ EMAC buffer memory is segmented into 128 byte units (not
+ configurable). This setting provides the number of such 128 byte
+ units used for reception. This is also equal to the number of RX
+ descriptors that will be allocated The selected value must be an
+ even power of 2.
+
+config SAMV7_EMAC0_NTXBUFFERS
+ int "Number of TX buffers"
+ default 8
+ ---help---
+ EMAC buffer memory is segmented into full Ethernet packets (size
+ NET_BUFSIZE bytes). This setting provides the number of such packets
+ that can be in flight. This is also equal to the number of TX
+ descriptors that will be allocated.
+
+config SAMV7_EMAC0_PHYADDR
+ int "PHY address"
+ default 1
+ ---help---
+ The 5-bit address of the PHY on the board. Default: 1
+
+config SAMV7_EMAC0_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
+ SAMV7_EMAC0_PHYINIT is defined in the configuration then the board specific logic must
+ provide sam_phyinitialize(); The SAMV7 EMAC driver will call this function
+ one time before it first uses the PHY.
+
+choice
+ prompt "PHY interface"
+ default SAMV7_EMAC0_MII
+
+config SAMV7_EMAC0_MII
+ bool "MII"
+ ---help---
+ Support Ethernet MII interface (vs RMII).
+
+config SAMV7_EMAC0_RMII
+ bool "RMII"
+ depends on !ARCH_CHIP_SAM4E
+ ---help---
+ Support Ethernet RMII interface (vs MII).
+
+endchoice # PHY interface
+
+config SAMV7_EMAC0_CLAUSE45
+ bool "Clause 45 MII"
+ depends on SAMV7_EMAC0_MII
+ ---help---
+ MDIO was originally defined in Clause 22 of IEEE RFC802.3. In the
+ original specification, a single MDIO interface is able to access up
+ to 32 registers in 32 different PHY devices. To meet the needs the
+ expanding needs of 10-Gigabit Ethernet devices, Clause 45 of the
+ 802.3ae specification provided the following additions to MDIO:
+
+ - Ability to access 65,536 registers in 32 different devices on
+ 32 different ports
+ - Additional OP-code and ST-code for Indirect Address register
+ access for 10 Gigabit Ethernet
+ - End-to-end fault signaling
+ - Multiple loopback points
+ - Low voltage electrical specification
+
+ By default, Clause 22 PHYs will be supported unless this option is
+ selected.
+
+config SAMV7_EMAC0_AUTONEG
+ bool "Use autonegotiation"
+ default y
+ ---help---
+ Use PHY autonegotiation to determine speed and mode
+
+config SAMV7_EMAC0_ETHFD
+ bool "Full duplex"
+ default n
+ depends on !SAMV7_EMAC0_AUTONEG
+ ---help---
+ If SAMV7_EMAC0_AUTONEG is not defined, then this may be defined to select full duplex
+ mode. Default: half-duplex
+
+config SAMV7_EMAC0_ETH100MBPS
+ bool "100 Mbps"
+ default n
+ depends on !SAMV7_EMAC0_AUTONEG
+ ---help---
+ If SAMV7_EMAC0_AUTONEG is not defined, then this may be defined to select 100 MBps
+ speed. Default: 10 Mbps
+
+config SAMV7_EMAC0_PHYSR
+ int "PHY Status Register Address (decimal)"
+ depends on SAMV7_EMAC0_AUTONEG
+ ---help---
+ This must be provided if SAMV7_EMAC0_AUTONEG is defined. The PHY status register
+ address may diff from PHY to PHY. This configuration sets the address of
+ the PHY status register.
+
+config SAMV7_EMAC0_PHYSR_ALTCONFIG
+ bool "PHY Status Alternate Bit Layout"
+ default n
+ depends on SAMV7_EMAC0_AUTONEG
+ ---help---
+ Different PHYs present speed and mode information in different ways. Some
+ will present separate information for speed and mode (this is the default).
+ Those PHYs, for example, may provide a 10/100 Mbps indication and a separate
+ full/half duplex indication. This options selects an alternative representation
+ where speed and mode information are combined. This might mean, for example,
+ separate bits for 10HD, 100HD, 10FD and 100FD.
+
+if SAMV7_EMAC0_AUTONEG
+if SAMV7_EMAC0_PHYSR_ALTCONFIG
+
+config SAMV7_EMAC0_PHYSR_ALTMODE
+ hex "PHY Mode Mask"
+ ---help---
+ This must be provided if SAMV7_EMAC0_AUTONEG is defined. This provide bit mask
+ for isolating the speed and full/half duplex mode bits.
+
+config SAMV7_EMAC0_PHYSR_10HD
+ hex "10MBase-T Half Duplex Value"
+ ---help---
+ This must be provided if SAMV7_EMAC0_AUTONEG is defined. This is the value
+ under the bit mask that represents the 10Mbps, half duplex setting.
+
+config SAMV7_EMAC0_PHYSR_100HD
+ hex "100Base-T Half Duplex Value"
+ ---help---
+ This must be provided if SAMV7_EMAC0_AUTONEG is defined. This is the value
+ under the bit mask that represents the 100Mbps, half duplex setting.
+
+config SAMV7_EMAC0_PHYSR_10FD
+ hex "10Base-T Full Duplex Value"
+ ---help---
+ This must be provided if SAMV7_EMAC0_AUTONEG is defined. This is the value
+ under the bit mask that represents the 10Mbps, full duplex setting.
+
+config SAMV7_EMAC0_PHYSR_100FD
+ hex "100Base-T Full Duplex Value"
+ ---help---
+ This must be provided if SAMV7_EMAC0_AUTONEG is defined. This is the value
+ under the bit mask that represents the 100Mbps, full duplex setting.
+
+endif # SAMV7_EMAC0_PHYSR_ALTCONFIG
+if !SAMV7_EMAC0_PHYSR_ALTCONFIG
+
+config SAMV7_EMAC0_PHYSR_SPEED
+ hex "PHY Speed Mask"
+ ---help---
+ This must be provided if SAMV7_EMAC0_AUTONEG is defined. This provides bit mask
+ for isolating the 10 or 100MBps speed indication.
+
+config SAMV7_EMAC0_PHYSR_100MBPS
+ hex "PHY 100Mbps Speed Value"
+ ---help---
+ This must be provided if SAMV7_EMAC0_AUTONEG is defined. This provides the value
+ of the speed bit(s) indicating 100MBps speed.
+
+config SAMV7_EMAC0_PHYSR_MODE
+ hex "PHY Mode Mask"
+ ---help---
+ This must be provided if SAMV7_EMAC0_AUTONEG is defined. This provides the
+ bit mask for isolating the full or half duplex mode bits.
+
+config SAMV7_EMAC0_PHYSR_FULLDUPLEX
+ hex "PHY Full Duplex Mode Value"
+ ---help---
+ This must be provided if SAMV7_EMAC0_AUTONEG is defined. This provides the
+ value of the mode bits indicating full duplex mode.
+
+endif # !SAMV7_EMAC0_PHYSR_ALTCONFIG
+endif # SAMV7_EMAC0_AUTONEG
+
+# These apply to both EMAC0 and EMAC1 (but are in the EMAC0 menu for now
+# because there is not yet any SAMV7 chip that supports two Ethernet MACS
+
+config SAMV7_EMAC0_ISETH0
+ bool
+ default y
+
+config SAMV7_EMAC_PREALLOCATE
+ bool "Preallocate buffers"
+ default n
+ ---help---
+ Buffer an descriptor many may either be allocated from the memory
+ pool or pre-allocated to lie in .bss. This options selected pre-
+ allocated buffer memory.
+
+config SAMV7_EMAC_NBC
+ bool "Disable Broadcast"
+ default n
+ ---help---
+ Select to disable receipt of broadcast packets.
+
+config SAMV7_EMAC_DEBUG
+ bool "Force EMAC0/1 DEBUG"
+ default n
+ depends on DEBUG && !DEBUG_NET
+ ---help---
+ This option will force debug output from EMAC driver even without
+ network debug output enabled. This is not normally something
+ that would want to do but is convenient if you are debugging the
+ driver and do not want to get overloaded with other
+ network-related debug output.
+
+config SAMV7_EMAC_REGDEBUG
+ bool "Register-Level Debug"
+ default n
+ depends on DEBUG
+ ---help---
+ Enable very low-level register access debug. Depends on DEBUG.
+
+endmenu # EMAC0 device driver options
diff --git a/nuttx/arch/arm/src/samv7/Make.defs b/nuttx/arch/arm/src/samv7/Make.defs
index 8dd76391d..868621c93 100644
--- a/nuttx/arch/arm/src/samv7/Make.defs
+++ b/nuttx/arch/arm/src/samv7/Make.defs
@@ -140,3 +140,7 @@ endif
ifeq ($(CONFIG_SAMV7_HSMCI),y)
CHIP_CSRCS += sam_hsmci.c sam_hsmci_clkdiv.c
endif
+
+ifeq ($(CONFIG_SAMV7_EMAC),y)
+CHIP_CSRCS += sam_emac.c sam_ethernet.c
+endif
diff --git a/nuttx/arch/arm/src/samv7/chip/samv71_pinmap.h b/nuttx/arch/arm/src/samv7/chip/samv71_pinmap.h
index 0c20598cf..f5dd8fc87 100644
--- a/nuttx/arch/arm/src/samv7/chip/samv71_pinmap.h
+++ b/nuttx/arch/arm/src/samv7/chip/samv71_pinmap.h
@@ -167,28 +167,28 @@
/* Ethernet MAC Controller (EMAC) */
-#define GPIO_EMAC_COL (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN13)
-#define GPIO_EMAC_CRS (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN10)
-#define GPIO_EMAC_MDC (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN8)
-#define GPIO_EMAC_MDIO (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN9)
-#define GPIO_EMAC_RX0 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN5)
-#define GPIO_EMAC_RX1 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN6)
-#define GPIO_EMAC_RX2 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN11)
-#define GPIO_EMAC_RX3 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN12)
-#define GPIO_EMAC_RXCK (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN14)
-#define GPIO_EMAC_RXDV (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN4)
-#define GPIO_EMAC_RXER (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN7)
-#define GPIO_EMAC_TSUCOMP_1 (GPIO_PERIPHB | GPIO_CFG_DEFAULT | GPIO_PORT_PIOB | GPIO_PIN1)
-#define GPIO_EMAC_TSUCOMP_2 (GPIO_PERIPHB | GPIO_CFG_DEFAULT | GPIO_PORT_PIOB | GPIO_PIN12)
-#define GPIO_EMAC_TSUCOMP_3 (GPIO_PERIPHC | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN11)
-#define GPIO_EMAC_TSUCOMP_4 (GPIO_PERIPHC | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN20)
-#define GPIO_EMAC_TX0 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN2)
-#define GPIO_EMAC_TX1 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN3)
-#define GPIO_EMAC_TX2 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN15)
-#define GPIO_EMAC_TX3 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN16)
-#define GPIO_EMAC_TXCK (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN0)
-#define GPIO_EMAC_TXEN (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN1)
-#define GPIO_EMAC_TXER (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN17)
+#define GPIO_EMAC0_COL (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN13)
+#define GPIO_EMAC0_CRS (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN10)
+#define GPIO_EMAC0_MDC (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN8)
+#define GPIO_EMAC0_MDIO (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN9)
+#define GPIO_EMAC0_RX0 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN5)
+#define GPIO_EMAC0_RX1 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN6)
+#define GPIO_EMAC0_RX2 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN11)
+#define GPIO_EMAC0_RX3 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN12)
+#define GPIO_EMAC0_RXCK (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN14)
+#define GPIO_EMAC0_RXDV (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN4)
+#define GPIO_EMAC0_RXER (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN7)
+#define GPIO_EMAC0_TSUCOMP_1 (GPIO_PERIPHB | GPIO_CFG_DEFAULT | GPIO_PORT_PIOB | GPIO_PIN1)
+#define GPIO_EMAC0_TSUCOMP_2 (GPIO_PERIPHB | GPIO_CFG_DEFAULT | GPIO_PORT_PIOB | GPIO_PIN12)
+#define GPIO_EMAC0_TSUCOMP_3 (GPIO_PERIPHC | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN11)
+#define GPIO_EMAC0_TSUCOMP_4 (GPIO_PERIPHC | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN20)
+#define GPIO_EMAC0_TX0 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN2)
+#define GPIO_EMAC0_TX1 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN3)
+#define GPIO_EMAC0_TX2 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN15)
+#define GPIO_EMAC0_TX3 (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN16)
+#define GPIO_EMAC0_TXCK (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN0)
+#define GPIO_EMAC0_TXEN (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN1)
+#define GPIO_EMAC0_TXER (GPIO_PERIPHA | GPIO_CFG_DEFAULT | GPIO_PORT_PIOD | GPIO_PIN17)
/* Image Sensor Interface (ISI) */
diff --git a/nuttx/arch/arm/src/samv7/sam_emac.c b/nuttx/arch/arm/src/samv7/sam_emac.c
new file mode 100644
index 000000000..8428df087
--- /dev/null
+++ b/nuttx/arch/arm/src/samv7/sam_emac.c
@@ -0,0 +1,4656 @@
+/****************************************************************************
+ * arch/arm/src/samv7/sam_emac.c
+ * 10/100 Base-T Ethernet driver for the SAMV71.
+ *
+ * Copyright (C) 2015 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * This logic derives from the SAMA5 Ethernet driver which, in turn, derived
+ * from the SAM4E Ethernet driver which used Atmel NoOS sample code for
+ * reference (only). That Atmel sample code has a BSD compatible license
+ * that requires this copyright notice:
+ *
+ * Copyright (c) 2012, Atmel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the names NuttX nor Atmel nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#if defined(CONFIG_DEBUG) && defined(CONFIG_SAMV7_EMAC_DEBUG)
+ /* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/wdog.h>
+#include <nuttx/kmalloc.h>
+
+#ifdef CONFIG_NET_NOINTS
+# include <nuttx/wqueue.h>
+#endif
+
+#include <nuttx/net/mii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/phy.h>
+
+#ifdef CONFIG_NET_PKT
+# include <nuttx/net/pkt.h>
+#endif
+
+#include <arch/samv7/chip.h>
+
+#include "up_arch.h"
+#include "up_internal.h"
+#include "cache.h"
+
+#include "chip/sam_pinmap.h"
+#include "sam_gpio.h"
+#include "sam_periphclks.h"
+#include "sam_ethernet.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_NET) && defined(CONFIG_SAMV7_EMAC)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+
+/* If processing is not done at the interrupt level, then high priority
+ * work queue support is required.
+ */
+
+#if defined(CONFIG_NET_NOINTS) && !defined(CONFIG_SCHED_HPWORK)
+# error High priority work queue support is required
+#endif
+
+/* EMAC0 Configuration ******************************************************/
+
+#ifdef CONFIG_SAMV7_EMAC0
+ /* Number of buffers for RX */
+
+# ifndef CONFIG_SAMV7_EMAC0_NRXBUFFERS
+# define CONFIG_SAMV7_EMAC0_NRXBUFFERS 16
+# endif
+
+ /* Number of buffers for TX */
+
+# ifndef CONFIG_SAMV7_EMAC0_NTXBUFFERS
+# define CONFIG_SAMV7_EMAC0_NTXBUFFERS 8
+# endif
+
+# ifndef CONFIG_SAMV7_EMAC0_PHYADDR
+# error "CONFIG_SAMV7_EMAC0_PHYADDR must be defined in the NuttX configuration"
+# endif
+
+# if !defined(CONFIG_SAMV7_EMAC0_MII) && !defined(CONFIG_SAMV7_EMAC0_RMII)
+# warning "Neither CONFIG_SAMV7_EMAC0_MII nor CONFIG_SAMV7_EMAC0_RMII defined"
+# endif
+
+# if defined(CONFIG_SAMV7_EMAC0_MII) && defined(CONFIG_SAMV7_EMAC0_RMII)
+# error "Both CONFIG_SAMV7_EMAC0_MII and CONFIG_SAMV7_EMAC0_RMII defined"
+# endif
+
+# ifndef CONFIG_SAMV7_EMAC0_PHYSR
+# error "CONFIG_SAMV7_EMAC0_PHYSR must be defined in the NuttX configuration"
+# endif
+
+# ifdef CONFIG_SAMV7_EMAC0_AUTONEG
+# ifdef CONFIG_SAMV7_EMAC0_PHYSR_ALTCONFIG
+# ifndef CONFIG_SAMV7_EMAC0_PHYSR_ALTMODE
+# error "CONFIG_SAMV7_EMAC0_PHYSR_ALTMODE must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC0_PHYSR_10HD
+# error "CONFIG_SAMV7_EMAC0_PHYSR_10HD must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC0_PHYSR_100HD
+# error "CONFIG_SAMV7_EMAC0_PHYSR_100HD must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC0_PHYSR_10FD
+# error "CONFIG_SAMV7_EMAC0_PHYSR_10FD must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC0_PHYSR_100FD
+# error "CONFIG_SAMV7_EMAC0_PHYSR_100FD must be defined in the NuttX configuration"
+# endif
+# else
+# ifndef CONFIG_SAMV7_EMAC0_PHYSR_SPEED
+# error "CONFIG_SAMV7_EMAC0_PHYSR_SPEED must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC0_PHYSR_100MBPS
+# error "CONFIG_SAMV7_EMAC0_PHYSR_100MBPS must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC0_PHYSR_MODE
+# error "CONFIG_SAMV7_EMAC0_PHYSR_MODE must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC0_PHYSR_FULLDUPLEX
+# error "CONFIG_SAMV7_EMAC0_PHYSR_FULLDUPLEX must be defined in the NuttX configuration"
+# endif
+# endif
+# endif
+
+ /* PHY definitions */
+
+# if defined(SAMV7_EMAC0_PHY_DM9161)
+# define EMAC0_MII_OUI_MSB 0x0181
+# define EMAC0_MII_OUI_LSB 0x2e
+# elif defined(SAMV7_EMAC0_PHY_LAN8700)
+# define EMAC0_MII_OUI_MSB 0x0007
+# define EMAC0_MII_OUI_LSB 0x30
+# elif defined(SAMV7_EMAC0_PHY_KSZ8051)
+# define EMAC0_MII_OUI_MSB 0x0022
+# define EMAC0_MII_OUI_LSB 0x05
+# elif defined(SAMV7_EMAC0_PHY_KSZ8061)
+# define EMAC0_MII_OUI_MSB 0x0022
+# define EMAC0_MII_OUI_LSB 0x05
+# elif defined(SAMV7_EMAC0_PHY_KSZ8081)
+# define EMAC0_MII_OUI_MSB 0x0022
+# define EMAC0_MII_OUI_LSB 0x05
+# else
+# error EMAC PHY unrecognized
+# endif
+#endif /* CONFIG_SAMV7_EMAC0 */
+
+/* EMAC1 Configuration ******************************************************/
+
+#ifdef CONFIG_SAMV7_EMAC1
+ /* Number of buffers for RX */
+
+# ifndef CONFIG_SAMV7_EMAC1_NRXBUFFERS
+# define CONFIG_SAMV7_EMAC1_NRXBUFFERS 16
+# endif
+
+ /* Number of buffers for TX */
+
+# ifndef CONFIG_SAMV7_EMAC1_NTXBUFFERS
+# define CONFIG_SAMV7_EMAC1_NTXBUFFERS 8
+# endif
+
+# ifndef CONFIG_SAMV7_EMAC1_PHYADDR
+# error "CONFIG_SAMV7_EMAC1_PHYADDR must be defined in the NuttX configuration"
+# endif
+
+# if !defined(CONFIG_SAMV7_EMAC1_MII) && !defined(CONFIG_SAMV7_EMAC1_RMII)
+# warning "Neither CONFIG_SAMV7_EMAC1_MII nor CONFIG_SAMV7_EMAC1_RMII defined"
+# endif
+
+# if defined(CONFIG_SAMV7_EMAC1_MII) && defined(CONFIG_SAMV7_EMAC1_RMII)
+# error "Both CONFIG_SAMV7_EMAC1_MII and CONFIG_SAMV7_EMAC1_RMII defined"
+# endif
+
+# ifndef CONFIG_SAMV7_EMAC1_PHYSR
+# error "CONFIG_SAMV7_EMAC1_PHYSR must be defined in the NuttX configuration"
+# endif
+
+# ifdef CONFIG_SAMV7_EMAC1_AUTONEG
+# ifdef CONFIG_SAMV7_EMAC1_PHYSR_ALTCONFIG
+# ifndef CONFIG_SAMV7_EMAC1_PHYSR_ALTMODE
+# error "CONFIG_SAMV7_EMAC1_PHYSR_ALTMODE must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC1_PHYSR_10HD
+# error "CONFIG_SAMV7_EMAC1_PHYSR_10HD must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC1_PHYSR_100HD
+# error "CONFIG_SAMV7_EMAC1_PHYSR_100HD must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC1_PHYSR_10FD
+# error "CONFIG_SAMV7_EMAC1_PHYSR_10FD must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC1_PHYSR_100FD
+# error "CONFIG_SAMV7_EMAC1_PHYSR_100FD must be defined in the NuttX configuration"
+# endif
+# else
+# ifndef CONFIG_SAMV7_EMAC1_PHYSR_SPEED
+# error "CONFIG_SAMV7_EMAC1_PHYSR_SPEED must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC1_PHYSR_100MBPS
+# error "CONFIG_SAMV7_EMAC1_PHYSR_100MBPS must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC1_PHYSR_MODE
+# error "CONFIG_SAMV7_EMAC1_PHYSR_MODE must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAMV7_EMAC1_PHYSR_FULLDUPLEX
+# error "CONFIG_SAMV7_EMAC1_PHYSR_FULLDUPLEX must be defined in the NuttX configuration"
+# endif
+# endif
+# endif
+
+ /* PHY definitions */
+
+# if defined(SAMV7_EMAC1_PHY_DM9161)
+# define EMAC1_MII_OUI_MSB 0x0181
+# define EMAC1_MII_OUI_LSB 0x2e
+# elif defined(SAMV7_EMAC1_PHY_LAN8700)
+# define EMAC1_MII_OUI_MSB 0x0007
+# define EMAC1_MII_OUI_LSB 0x30
+# elif defined(SAMV7_EMAC1_PHY_KSZ8051)
+# define EMAC1_MII_OUI_MSB 0x0022
+# define EMAC1_MII_OUI_LSB 0x05
+# elif defined(SAMV7_EMAC1_PHY_KSZ8061)
+# define EMAC1_MII_OUI_MSB 0x0022
+# define EMAC1_MII_OUI_LSB 0x05
+# elif defined(SAMV7_EMAC1_PHY_KSZ8081)
+# define EMAC1_MII_OUI_MSB 0x0022
+# define EMAC1_MII_OUI_LSB 0x05
+# else
+# error EMAC PHY unrecognized
+# endif
+#endif /* CONFIG_SAMV7_EMAC1 */
+
+/* Common Configuration *****************************************************/
+
+#undef CONFIG_SAMV7_EMAC_NBC
+
+/* Extremely detailed register debug that you would normally never want
+ * enabled.
+ */
+
+#ifndef CONFIG_DEBUG
+# undef CONFIG_SAMV7_EMAC_REGDEBUG
+#endif
+
+#ifdef CONFIG_NET_DUMPPACKET
+# define sam_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+# define sam_dumppacket(m,a,n)
+#endif
+
+/* EMAC buffer sizes, number of buffers, and number of descriptors **********
+ *
+ * REVISIT: The CONFIG_NET_MULTIBUFFER might be useful. It might be possible
+ * to use this option to send and receive messages directly into the DMA
+ * buffers, saving a copy. There might be complications on the receiving
+ * side, however, where buffers may wrap and where the size of the received
+ * frame will typically be smaller than a full packet.
+ */
+
+#ifdef CONFIG_NET_MULTIBUFFER
+# error CONFIG_NET_MULTIBUFFER must not be set
+#endif
+
+#define EMAC_RX_UNITSIZE 128 /* Fixed size for RX buffer */
+#define EMAC_TX_UNITSIZE CONFIG_NET_ETH_MTU /* MAX size for Ethernet packet */
+
+/* These cache operations are not yet available */
+
+#undef HAVE_CLEAN_DCACHE_RANGE
+#undef HAVE_INVALIDATE_DCACHE_RANGE
+
+/* Timing *******************************************************************/
+/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per
+ * second
+ */
+
+#define SAM_WDDELAY (1*CLK_TCK)
+#define SAM_POLLHSEC (1*2)
+
+/* TX timeout = 1 minute */
+
+#define SAM_TXTIMEOUT (60*CLK_TCK)
+
+/* PHY read/write delays in loop counts */
+
+#define PHY_RETRY_MAX 1000000
+
+/* Helpers ******************************************************************/
+/* This is a helper pointer for accessing the contents of the EMAC
+ * header
+ */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+/* This structure defines the constant an configured attributes of an EMAC */
+
+struct sam_emacattr_s
+{
+ /* Basic hardware information */
+
+ uint32_t base; /* EMAC Register base address */
+ xcpt_t handler; /* EMAC interrupt handler */
+ uint8_t emac; /* EMACn, n=0 or 1 */
+ uint8_t irq; /* EMAC interrupt number */
+
+ /* PHY Configuration */
+
+ uint8_t phyaddr; /* PHY address */
+ uint8_t physr; /* PHY status register address */
+ uint16_t msoui; /* MS 16 bits of the 18-bit OUI */
+ uint8_t lsoui; /* LS 2 bits of the 18-bit OUI */
+ bool rmii; /* True: RMII vs. False: MII */
+ bool clause45; /* True: Clause 45 behavior */
+//bool autoneg; /* True: Autonegotiate rate and *plex */
+ bool sralt; /* True: Alternate PHYSR bit access */
+
+ union
+ {
+ /* "Standard" form: Individual bits determine speed and half/full
+ * duplex.
+ */
+
+ struct
+ {
+ uint16_t stdmask; /* Mask for speed and *plex mode bits */
+ uint16_t speed100; /* 100Base_t bit */
+ uint16_t fduplex; /* Full duplex bit */
+ } std;
+
+ /* Alternative form: Speed and duplex are encoded in a single,
+ * multi-bit field.
+ */
+
+ struct
+ {
+ uint16_t altmask; /* Mask speed for mode bits */
+ uint16_t hdx10; /* 10Base_T Half Duplex bit pattern */
+ uint16_t hdx100; /* 100Base_T Half Duplex bit pattern */
+ uint16_t fdx10; /* 10Base_T Half Duplex bit pattern */
+ uint16_t fdx100; /* 100Base_T Half Duplex bit pattern */
+ } alt;
+ } u;
+
+ /* Buffer and descriptor configuration */
+
+ uint8_t ntxbuffers; /* Number of TX buffers */
+ uint8_t nrxbuffers; /* Number of RX buffers */
+
+#ifdef CONFIG_SAMV7_EMAC_PREALLOCATE
+ /* Attributes and addresses of preallocated buffers */
+
+ struct emac_txdesc_s *txdesc; /* Preallocated TX descriptor list */
+ struct emac_rxdesc_s *rxdesc; /* Preallocated RX descriptor list */
+ uint8_t *txbuffer; /* Preallocated TX buffers */
+ uint8_t *rxbuffer; /* Preallocated RX buffers */
+#endif
+};
+
+/* The sam_emac_s encapsulates all state information for the EMAC peripheral */
+
+struct sam_emac_s
+{
+ uint8_t ifup : 1; /* true:ifup false:ifdown */
+ WDOG_ID txpoll; /* TX poll timer */
+ WDOG_ID txtimeout; /* TX timeout timer */
+#ifdef CONFIG_NET_NOINTS
+ struct work_s work; /* For deferring work to the work queue */
+#endif
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct net_driver_s dev; /* Interface understood by uIP */
+
+ /* Constant and configured attributes of the EMAC */
+
+ const struct sam_emacattr_s *attr;
+
+ /* Used to track transmit and receive descriptors */
+
+ uint8_t phyaddr; /* PHY address (pre-defined by pins on reset) */
+#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT)
+ uint8_t phytype; /* See SAMV7_EMAC0/1_PHY_TYPE definitions */
+#endif
+ uint16_t txhead; /* Circular buffer head index */
+ uint16_t txtail; /* Circular buffer tail index */
+ uint16_t rxndx; /* RX index for current processing RX descriptor */
+
+ uint8_t *rxbuffer; /* Allocated RX buffers */
+ uint8_t *txbuffer; /* Allocated TX buffers */
+ struct emac_rxdesc_s *rxdesc; /* Allocated RX descriptors */
+ struct emac_txdesc_s *txdesc; /* Allocated TX descriptors */
+
+ /* Debug stuff */
+
+#ifdef CONFIG_SAMV7_EMAC_REGDEBUG
+ bool wrlast; /* Last was a write */
+ uintptr_t addrlast; /* Last address */
+ uint32_t vallast; /* Last value */
+ int ntimes; /* Number of times */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* Register operations ******************************************************/
+
+#if defined(CONFIG_SAMV7_EMAC_REGDEBUG) && defined(CONFIG_DEBUG)
+static bool sam_checkreg(struct sam_emac_s *priv, bool wr,
+ uint32_t regval, uintptr_t address);
+#endif
+
+static uint32_t sam_getreg(struct sam_emac_s *priv, uint16_t offset);
+static void sam_putreg(struct sam_emac_s *priv, uint16_t offset, uint32_t val);
+
+/* Buffer management */
+
+static uint16_t sam_txinuse(struct sam_emac_s *priv);
+static uint16_t sam_txfree(struct sam_emac_s *priv);
+static int sam_buffer_initialize(struct sam_emac_s *priv);
+static void sam_buffer_free(struct sam_emac_s *priv);
+
+/* Common TX logic */
+
+static int sam_transmit(struct sam_emac_s *priv);
+static int sam_txpoll(struct net_driver_s *dev);
+static void sam_dopoll(struct sam_emac_s *priv);
+
+/* Interrupt handling */
+
+static int sam_recvframe(struct sam_emac_s *priv);
+static void sam_receive(struct sam_emac_s *priv);
+static void sam_txdone(struct sam_emac_s *priv);
+static inline void sam_interrupt_process(FAR struct sam_emac_s *priv);
+#ifdef CONFIG_NET_NOINTS
+static void sam_interrupt_work(FAR void *arg);
+#endif
+static int sam_emac_interrupt(struct sam_emac_s *priv);
+#ifdef CONFIG_SAMV7_EMAC0
+static int sam_emac0_interrupt(int irq, void *context);
+#endif
+#ifdef CONFIG_SAMV7_EMAC1
+static int sam_emac1_interrupt(int irq, void *context);
+#endif
+
+/* Watchdog timer expirations */
+
+static inline void sam_txtimeout_process(FAR struct sam_emac_s *priv);
+#ifdef CONFIG_NET_NOINTS
+static void sam_txtimeout_work(FAR void *arg);
+#endif
+static void sam_txtimeout_expiry(int argc, uint32_t arg, ...);
+
+static inline void sam_poll_process(FAR struct sam_emac_s *priv);
+#ifdef CONFIG_NET_NOINTS
+static void sam_poll_work(FAR void *arg);
+#endif
+static void sam_poll_expiry(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int sam_ifup(struct net_driver_s *dev);
+static int sam_ifdown(struct net_driver_s *dev);
+
+static inline void sam_txavail_process(FAR struct sam_emac_s *priv);
+#ifdef CONFIG_NET_NOINTS
+static void sam_txavail_work(FAR void *arg);
+#endif
+static int sam_txavail(struct net_driver_s *dev);
+
+#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int sam_hashindx(const uint8_t *mac);
+static int sam_addmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+#ifdef CONFIG_NET_IGMP
+static int sam_rmmac(struct net_driver_s *dev, const uint8_t *mac);
+#endif
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int sam_ioctl(struct net_driver_s *dev, int cmd, long arg);
+#endif
+
+/* PHY Initialization */
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_VERBOSE)
+static void sam_phydump(struct sam_emac_s *priv);
+#else
+# define sam_phydump(priv)
+#endif
+
+#if 0 /* Not used */
+static bool sam_is10hdx(struct sam_emac_s *priv, uint16_t physr);
+#endif
+static bool sam_is100hdx(struct sam_emac_s *priv, uint16_t physr);
+static bool sam_is10fdx(struct sam_emac_s *priv, uint16_t physr);
+static bool sam_is100fdx(struct sam_emac_s *priv, uint16_t physr);
+
+#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT)
+static int sam_phyintenable(struct sam_emac_s *priv);
+#endif
+static int sam_phywait(struct sam_emac_s *priv);
+static int sam_phyreset(struct sam_emac_s *priv);
+static int sam_phyfind(struct sam_emac_s *priv, uint8_t *phyaddr);
+static int sam_phyread(struct sam_emac_s *priv, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t *phyval);
+static int sam_phywrite(struct sam_emac_s *priv, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t phyval);
+static int sam_autonegotiate(struct sam_emac_s *priv);
+static bool sam_linkup(struct sam_emac_s *priv);
+static int sam_phyinit(struct sam_emac_s *priv);
+
+/* EMAC Initialization */
+
+static void sam_txreset(struct sam_emac_s *priv);
+static void sam_rxreset(struct sam_emac_s *priv);
+static void sam_emac_enableclk(struct sam_emac_s *priv);
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+static void sam_emac_disableclk(struct sam_emac_s *priv);
+#endif
+static void sam_emac_reset(struct sam_emac_s *priv);
+static void sam_macaddress(struct sam_emac_s *priv);
+#ifdef CONFIG_NET_ICMPv6
+static void sam_ipv6multicast(struct sam_emac_s *priv);
+#endif
+static int sam_emac_configure(struct sam_emac_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMV7_EMAC_PREALLOCATE
+/* Preallocated data */
+
+#ifdef CONFIG_SAMV7_EMAC0
+/* EMAC0 TX descriptors list */
+
+static struct emac_txdesc_s g_emac0_txdesc[CONFIG_SAMV7_EMAC0_NTXBUFFERS]
+ __attribute__((aligned(8)));
+
+/* EMAC0 RX descriptors list */
+
+static struct emac_rxdesc_s g_emac0_rxdesc[CONFIG_SAMV7_EMAC0_NRXBUFFERS]
+ __attribute__((aligned(8)));
+
+/* EMAC0 Transmit Buffers
+ *
+ * Section 3.6 of AMBA 2.0 spec states that burst should not cross 1K Boundaries.
+ * Receive buffer manager writes are burst of 2 words => 3 lsb bits of the address
+ * shall be set to 0
+ */
+
+static uint8_t g_emac0_txbuffer[CONFIG_SAMV7_EMAC0_NTXBUFFERS * EMAC_TX_UNITSIZE];
+ __attribute__((aligned(8)))
+
+/* EMAC0 Receive Buffers */
+
+static uint8_t g_emac0_rxbuffer[CONFIG_SAMV7_EMAC0_NRXBUFFERS * EMAC_RX_UNITSIZE]
+ __attribute__((aligned(8)));
+
+#endif
+
+#ifdef CONFIG_SAMV7_EMAC1
+/* EMAC1 TX descriptors list */
+
+static struct emac_txdesc_s g_emac1_txdesc[CONFIG_SAMV7_EMAC1_NTXBUFFERS]
+ __attribute__((aligned(8)));
+
+/* EMAC1 RX descriptors list */
+
+static struct emac_rxdesc_s g_emac1_rxdesc[CONFIG_SAMV7_EMAC1_NRXBUFFERS]
+ __attribute__((aligned(8)));
+
+/* EMAC1 Transmit Buffers
+ *
+ * Section 3.6 of AMBA 2.0 spec states that burst should not cross 1K Boundaries.
+ * Receive buffer manager writes are burst of 2 words => 3 lsb bits of the address
+ * shall be set to 0
+ */
+
+static uint8_t g_emac1_txbuffer[CONFIG_SAMV7_EMAC1_NTXBUFFERS * EMAC_TX_UNITSIZE];
+ __attribute__((aligned(8)))
+
+/* EMAC1 Receive Buffers */
+
+static uint8_t g_emac1_rxbuffer[CONFIG_SAMV7_EMAC1_NRXBUFFERS * EMAC_RX_UNITSIZE]
+ __attribute__((aligned(8)));
+
+#endif
+#endif
+
+/* The driver state singletons */
+
+#ifdef CONFIG_SAMV7_EMAC0
+static const struct sam_emacattr_s g_emac0_attr =
+{
+ /* Basic hardware information */
+
+ .base = SAM_EMAC0_BASE,
+ .handler = sam_emac0_interrupt,
+ .emac = EMAC0_INTF,
+ .irq = SAM_IRQ_EMAC0,
+
+ /* PHY Configuration */
+
+ .phyaddr = CONFIG_SAMV7_EMAC0_PHYADDR,
+ .physr = CONFIG_SAMV7_EMAC0_PHYSR,
+ .msoui = EMAC0_MII_OUI_MSB,
+ .lsoui = EMAC0_MII_OUI_LSB,
+#ifdef CONFIG_SAMV7_EMAC0_RMII
+ .rmii = true,
+#endif
+#ifdef CONFIG_SAMV7_EMAC0_CLAUSE45
+ .clause45 = true,
+#endif
+#ifdef CONFIG_SAMV7_EMAC0_AUTONEG
+//.autoneg = true,
+#endif
+#ifdef CONFIG_SAMV7_EMAC0_PHYSR_ALTCONFIG
+ .sralt = true,
+#endif
+
+ .u =
+ {
+#ifdef CONFIG_SAMV7_EMAC0_PHYSR_ALTCONFIG
+ .alt =
+ {
+ .altmask = CONFIG_SAMV7_EMAC0_PHYSR_ALTMODE,
+ .hdx10 = CONFIG_SAMV7_EMAC0_PHYSR_10HD,
+ .hdx100 = CONFIG_SAMV7_EMAC0_PHYSR_100HD,
+ .fdx10 = CONFIG_SAMV7_EMAC0_PHYSR_10FD,
+ .fdx100 = CONFIG_SAMV7_EMAC0_PHYSR_100FD,
+ },
+#else
+ .std =
+ {
+ .stdmask = (CONFIG_SAMV7_EMAC0_PHYSR_SPEED | CONFIG_SAMV7_EMAC0_PHYSR_MODE),
+ .speed100 = CONFIG_SAMV7_EMAC0_PHYSR_100MBPS,
+ .fduplex = CONFIG_SAMV7_EMAC0_PHYSR_FULLDUPLEX,
+ },
+#endif
+ },
+
+ /* Buffer and descriptor configuration */
+
+ .ntxbuffers = CONFIG_SAMV7_EMAC0_NTXBUFFERS,
+ .nrxbuffers = CONFIG_SAMV7_EMAC0_NRXBUFFERS,
+
+#ifdef CONFIG_SAMV7_EMAC_PREALLOCATE
+ /* Addresses of preallocated buffers */
+
+ .txdesc = g_emac0_txdesc,
+ .rxdesc = g_emac0_rxdesc,
+ .txbuffer = g_emac0_txbuffer,
+ .rxbuffer = g_emac0_rxbuffer,
+#endif
+};
+
+static struct sam_emac_s g_emac0;
+#endif
+
+#ifdef CONFIG_SAMV7_EMAC1
+static const struct sam_emacattr_s g_emac1_attr =
+{
+ /* Basic hardware information */
+
+ .base = SAM_EMAC1_BASE,
+ .handler = sam_emac1_interrupt,
+ .emac = EMAC1_INTF,
+ .irq = SAM_IRQ_EMAC1,
+
+ /* PHY Configuration */
+
+ .phyaddr = CONFIG_SAMV7_EMAC1_PHYADDR,
+ .physr = CONFIG_SAMV7_EMAC1_PHYSR,
+ .msoui = EMAC1_MII_OUI_MSB,
+ .lsoui = EMAC1_MII_OUI_LSB,
+#ifdef CONFIG_SAMV7_EMAC1_RMII
+ .rmii = true,
+#endif
+#ifdef CONFIG_SAMV7_EMAC1_CLAUSE45
+ .clause45 = true,
+#endif
+#ifdef CONFIG_SAMV7_EMAC1_AUTONEG
+//.autoneg = true,
+#endif
+#ifdef CONFIG_SAMV7_EMAC1_PHYSR_ALTCONFIG
+ .sralt = true,
+#endif
+
+ .u =
+ {
+#ifdef CONFIG_SAMV7_EMAC1_PHYSR_ALTCONFIG
+ .alt =
+ {
+ .altmask = CONFIG_SAMV7_EMAC1_PHYSR_ALTMODE,
+ .hdx10 = CONFIG_SAMV7_EMAC1_PHYSR_10HD,
+ .hdx100 = CONFIG_SAMV7_EMAC1_PHYSR_100HD,
+ .fdx10 = CONFIG_SAMV7_EMAC1_PHYSR_10FD,
+ .fdx100 = CONFIG_SAMV7_EMAC1_PHYSR_100FD,
+ },
+#else
+ .std =
+ {
+ .stdmask = (CONFIG_SAMV7_EMAC1_PHYSR_SPEED | CONFIG_SAMV7_EMAC1_PHYSR_MODE),
+ .speed100 = CONFIG_SAMV7_EMAC1_PHYSR_100MBPS,
+ .fduplex = CONFIG_SAMV7_EMAC1_PHYSR_FULLDUPLEX,
+ },
+#endif
+ },
+
+ /* Buffer and descriptor configuration */
+
+ .ntxbuffers = CONFIG_SAMV7_EMAC1_NTXBUFFERS,
+ .nrxbuffers = CONFIG_SAMV7_EMAC1_NRXBUFFERS,
+
+#ifdef CONFIG_SAMV7_EMAC_PREALLOCATE
+ /* Attributes and addresses of preallocated buffers */
+
+ .txdesc = g_emac1_txdesc,
+ .rxdesc = g_emac1_rxdesc,
+ .txbuffer = g_emac1_txbuffer,
+ .rxbuffer = g_emac1_rxbuffer,
+#endif
+};
+
+static struct sam_emac_s g_emac1;
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+/****************************************************************************
+ * Name: sam_checkreg
+ *
+ * Description:
+ * Check if the current register access is a duplicate of the preceding.
+ *
+ * Input Parameters:
+ * regval - The value to be written
+ * address - The address of the register to write to
+ *
+ * Returned Value:
+ * true: This is the first register access of this type.
+ * flase: This is the same as the preceding register access.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMV7_EMAC_REGDEBUG
+static bool sam_checkreg(struct sam_emac_s *priv, bool wr, uint32_t regval,
+ uintptr_t regaddr)
+{
+ if (wr == priv->wrlast && /* Same kind of access? */
+ regval == priv->vallast && /* Same value? */
+ regaddr == priv->addrlast) /* Same address? */
+ {
+ /* Yes, then just keep a count of the number of times we did this. */
+
+ priv->ntimes++;
+ return false;
+ }
+ else
+ {
+ /* Did we do the previous operation more than once? */
+
+ if (priv->ntimes > 0)
+ {
+ /* Yes... show how many times we did it */
+
+ lldbg("...[Repeats %d times]...\n", priv->ntimes);
+ }
+
+ /* Save information about the new access */
+
+ priv->wrlast = wr;
+ priv->vallast = regval;
+ priv->addrlast = regaddr;
+ priv->ntimes = 0;
+ }
+
+ /* Return true if this is the first time that we have done this operation */
+
+ return true;
+}
+#endif
+
+/****************************************************************************
+ * Name: sam_getreg
+ *
+ * Description:
+ * Read a 32-bit EMAC register using an offset from the EMAC base address
+ *
+ ****************************************************************************/
+
+static uint32_t sam_getreg(struct sam_emac_s *priv, uint16_t offset)
+{
+ uintptr_t regaddr = priv->attr->base + (uintptr_t)offset;
+ uint32_t regval = getreg32(regaddr);
+
+#ifdef CONFIG_SAMV7_EMAC_REGDEBUG
+ if (sam_checkreg(priv, false, regval, regaddr))
+ {
+ lldbg("%08x->%08x\n", regaddr, regval);
+ }
+#endif
+
+ return regval;
+}
+
+/****************************************************************************
+ * Name: sam_putreg
+ *
+ * Description:
+ * Write to a 32-bit EMAC register using an offset from the EMAC base
+ * address
+ *
+ ****************************************************************************/
+
+static void sam_putreg(struct sam_emac_s *priv, uint16_t offset,
+ uint32_t regval)
+{
+ uintptr_t regaddr = priv->attr->base + (uintptr_t)offset;
+
+#ifdef CONFIG_SAMV7_EMAC_REGDEBUG
+ if (sam_checkreg(priv, true, regval, regaddr))
+ {
+ lldbg("%08x<-%08x\n", regaddr, regval);
+ }
+#endif
+
+ putreg32(regval, regaddr);
+}
+
+/****************************************************************************
+ * Function: sam_txinuse
+ *
+ * Description:
+ * Return the number of TX buffers in-use
+ *
+ * Input Parameters:
+ * priv - The EMAC driver state
+ *
+ * Returned Value:
+ * The number of TX buffers in-use
+ *
+ ****************************************************************************/
+
+static uint16_t sam_txinuse(struct sam_emac_s *priv)
+{
+ uint32_t txhead32 = (uint32_t)priv->txhead;
+ if ((uint32_t)priv->txtail > txhead32)
+ {
+ txhead32 += priv->attr->ntxbuffers;
+ }
+
+ return (uint16_t)(txhead32 - (uint32_t)priv->txtail);
+}
+
+/****************************************************************************
+ * Function: sam_txfree
+ *
+ * Description:
+ * Return the number of TX buffers available
+ *
+ * Input Parameters:
+ * priv - The EMAC driver state
+ *
+ * Returned Value:
+ * The number of TX buffers available
+ *
+ ****************************************************************************/
+
+static uint16_t sam_txfree(struct sam_emac_s *priv)
+{
+ /* The number available is equal to the total number of buffers, minus the
+ * number of buffers in use. Notice that that actual number of buffers is
+ * the configured size minus 1.
+ */
+
+ return (priv->attr->ntxbuffers-1) - sam_txinuse(priv);
+}
+
+/****************************************************************************
+ * Function: sam_buffer_initialize
+ *
+ * Description:
+ * Allocate aligned TX and RX descriptors and buffers. For the case of
+ * pre-allocated structures, the function degenerates to a few assignements.
+ *
+ * Input Parameters:
+ * priv - The EMAC driver state
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ * Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int sam_buffer_initialize(struct sam_emac_s *priv)
+{
+#ifdef CONFIG_SAMV7_EMAC_PREALLOCATE
+ /* Use pre-allocated buffers */
+
+ priv->txdesc = priv->attr->txdesc;
+ priv->rxdesc = priv->attr->rxdesc;
+ priv->txbuffer = priv->attr->txbuffer;
+ priv->rxbuffer = priv->attr->rxbuffer;
+
+#else
+ size_t allocsize;
+
+ /* Allocate buffers */
+
+ allocsize = priv->attr->ntxbuffers * sizeof(struct emac_txdesc_s);
+ priv->txdesc = (struct emac_txdesc_s *)kmm_memalign(8, allocsize);
+ if (!priv->txdesc)
+ {
+ nlldbg("ERROR: Failed to allocate TX descriptors\n");
+ return -ENOMEM;
+ }
+
+ memset(priv->txdesc, 0, allocsize);
+
+ allocsize = priv->attr->nrxbuffers * sizeof(struct emac_rxdesc_s);
+ priv->rxdesc = (struct emac_rxdesc_s *)kmm_memalign(8, allocsize);
+ if (!priv->rxdesc)
+ {
+ nlldbg("ERROR: Failed to allocate RX descriptors\n");
+ sam_buffer_free(priv);
+ return -ENOMEM;
+ }
+
+ memset(priv->rxdesc, 0, allocsize);
+
+ allocsize = priv->attr->ntxbuffers * EMAC_TX_UNITSIZE;
+ priv->txbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+ if (!priv->txbuffer)
+ {
+ nlldbg("ERROR: Failed to allocate TX buffer\n");
+ sam_buffer_free(priv);
+ return -ENOMEM;
+ }
+
+ allocsize = priv->attr->nrxbuffers * EMAC_RX_UNITSIZE;
+ priv->rxbuffer = (uint8_t *)kmm_memalign(8, allocsize);
+ if (!priv->rxbuffer)
+ {
+ nlldbg("ERROR: Failed to allocate RX buffer\n");
+ sam_buffer_free(priv);
+ return -ENOMEM;
+ }
+
+#endif
+
+ DEBUGASSERT(((uintptr_t)priv->rxdesc & 7) == 0 &&
+ ((uintptr_t)priv->rxbuffer & 7) == 0 &&
+ ((uintptr_t)priv->txdesc & 7) == 0 &&
+ ((uintptr_t)priv->txbuffer & 7) == 0);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: sam_buffer_free
+ *
+ * Description:
+ * Free aligned TX and RX descriptors and buffers. For the case of
+ * pre-allocated structures, the function does nothing.
+ *
+ * Input Parameters:
+ * priv - The EMAC driver state
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void sam_buffer_free(struct sam_emac_s *priv)
+{
+#ifndef CONFIG_SAMV7_EMAC_PREALLOCATE
+ /* Free allocated buffers */
+
+ if (priv->txdesc)
+ {
+ kmm_free(priv->txdesc);
+ priv->txdesc = NULL;
+ }
+
+ if (priv->rxdesc)
+ {
+ kmm_free(priv->rxdesc);
+ priv->rxdesc = NULL;
+ }
+
+ if (priv->txbuffer)
+ {
+ kmm_free(priv->txbuffer);
+ priv->txbuffer = NULL;
+ }
+
+ if (priv->rxbuffer)
+ {
+ kmm_free(priv->rxbuffer);
+ priv->rxbuffer = NULL;
+ }
+#endif
+}
+
+/****************************************************************************
+ * Function: sam_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * 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 sam_transmit(struct sam_emac_s *priv)
+{
+ struct net_driver_s *dev = &priv->dev;
+ volatile struct emac_txdesc_s *txdesc;
+ uint32_t regval;
+ uint32_t status;
+
+ nllvdbg("d_len: %d txhead: %d\n", dev->d_len, priv->txhead);
+ sam_dumppacket("Transmit packet", dev->d_buf, dev->d_len);
+
+ /* Check parameter */
+
+ if (dev->d_len > EMAC_TX_UNITSIZE)
+ {
+ nlldbg("ERROR: Packet too big: %d\n", dev->d_len);
+ return -EINVAL;
+ }
+
+ /* Pointer to the current TX descriptor */
+
+ txdesc = &priv->txdesc[priv->txhead];
+
+ /* If no free TX descriptor, buffer can't be sent */
+
+ if (sam_txfree(priv) < 1)
+ {
+ nlldbg("ERROR: No free TX descriptors\n");
+ return -EBUSY;
+ }
+
+ /* Setup/Copy data to transmission buffer */
+
+ if (dev->d_len > 0)
+ {
+ /* Driver managed the ring buffer */
+
+ memcpy((void *)txdesc->addr, dev->d_buf, dev->d_len);
+
+#ifdef HAVE_CLEAN_DCACHE_RANGE
+ arch_clean_dcache((uintptr_t)txdesc->addr,
+ (uintptr_t)txdesc->addr + dev->d_len);
+#endif
+ }
+
+ /* Update TX descriptor status. */
+
+ status = dev->d_len | EMACTXD_STA_LAST;
+ if (priv->txhead == priv->attr->ntxbuffers-1)
+ {
+ status |= EMACTXD_STA_WRAP;
+ }
+
+ /* Update the descriptor status and flush the updated value to RAM */
+
+ txdesc->status = status;
+
+#ifdef HAVE_CLEAN_DCACHE_RANGE
+ arch_clean_dcache((uint32_t)txdesc,
+ (uint32_t)txdesc + sizeof(struct emac_txdesc_s));
+#else
+ arch_clean_dcache_all();
+#endif
+
+ /* Increment the head index */
+
+ if (++priv->txhead >= priv->attr->ntxbuffers)
+ {
+ priv->txhead = 0;
+ }
+
+ /* Now start transmission (if it is not already done) */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval |= EMAC_NCR_TSTART;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ (void)wd_start(priv->txtimeout, SAM_TXTIMEOUT, sam_txtimeout_expiry, 1,
+ (uint32_t)priv);
+
+ /* Set d_len to zero meaning that the d_buf[] packet buffer is again
+ * available.
+ */
+
+ dev->d_len = 0;
+
+ /* If we have no more available TX descriptors, then we must disable the
+ * RCOMP interrupt to stop further RX processing. Why? Because EACH RX
+ * packet that is dispatched is also an opportunity to replay with a TX
+ * packet. So, if we cannot handle an RX packet reply, then we disable
+ * all RX packet processing.
+ */
+
+ if (sam_txfree(priv) < 1)
+ {
+ nllvdbg("Disabling RX interrupts\n");
+ sam_putreg(priv, SAM_EMAC_IDR_OFFSET, EMAC_INT_RCOMP);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: sam_txpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP 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
+ *
+ * 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 sam_txpoll(struct net_driver_s *dev)
+{
+ struct sam_emac_s *priv = (struct sam_emac_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 */
+
+ /* Send the packet */
+
+ sam_transmit(priv);
+
+ /* Check if the there are any free TX descriptors. We cannot perform
+ * the TX poll if we do not have buffering for another packet.
+ */
+
+ if (sam_txfree(priv) == 0)
+ {
+ /* We have to terminate the poll if we have no more descriptors
+ * available for another transfer.
+ */
+
+ return -EBUSY;
+ }
+ }
+
+ /* If zero is returned, the polling will continue until all connections have
+ * been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: sam_dopoll
+ *
+ * Description:
+ * Perform the uIP poll.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void sam_dopoll(struct sam_emac_s *priv)
+{
+ struct net_driver_s *dev = &priv->dev;
+
+ /* Check if the there are any free TX descriptors. We cannot perform the
+ * TX poll if we do not have buffering for another packet.
+ */
+
+ if (sam_txfree(priv) > 0)
+ {
+ /* If we have the descriptor, then poll uIP for new XMIT data. */
+
+ (void)devif_poll(dev, sam_txpoll);
+ }
+}
+
+/****************************************************************************
+ * Function: sam_recvframe
+ *
+ * Description:
+ * The function is called when a frame is received. It scans the RX
+ * descriptors of the received frame and assembles the full packet/
+ *
+ * NOTE: This function will silently discard any packets containing errors.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * OK if a packet was successfully returned; -EAGAIN if there are no
+ * further packets available
+ *
+ * Assumptions:
+ * - Global interrupts are disabled by interrupt handling logic.
+ * - The RX descriptor D-cache list has been invalided to force fetching
+ * from RAM.
+ *
+ ****************************************************************************/
+
+static int sam_recvframe(struct sam_emac_s *priv)
+{
+ struct emac_rxdesc_s *rxdesc;
+ struct net_driver_s *dev;
+ const uint8_t *src;
+ uint8_t *dest;
+ uint32_t rxndx;
+ uint32_t pktlen;
+ uint16_t copylen;
+ bool isframe;
+
+ /* Process received RX descriptor. The ownership bit is set by the EMAC
+ * once it has successfully written a frame to memory.
+ */
+
+ dev = &priv->dev;
+ dev->d_len = 0;
+
+ dest = dev->d_buf;
+ pktlen = 0;
+
+ rxndx = priv->rxndx;
+ rxdesc = &priv->rxdesc[rxndx];
+ isframe = false;
+
+ /* Invalidate the RX descriptor to force re-fetching from RAM */
+
+#warning FIXME!!!
+#ifdef HAVE_INVALIDATE_DCACHE_RANGE /* Revisit */
+ arch_invalidate_dcache((uintptr_t)rxdesc,
+ (uintptr_t)rxdesc + sizeof(struct emac_rxdesc_s));
+#endif
+
+ nllvdbg("rxndx: %d\n", rxndx);
+
+ while ((rxdesc->addr & EMACRXD_ADDR_OWNER) != 0)
+ {
+ /* The start of frame bit indicates the beginning of a frame. Discard
+ * any previous fragments.
+ */
+
+ if ((rxdesc->status & EMACRXD_STA_SOF) != 0)
+ {
+ /* Skip previous fragments */
+
+ while (rxndx != priv->rxndx)
+ {
+ /* Give ownership back to the EMAC */
+
+ rxdesc = &priv->rxdesc[priv->rxndx];
+ rxdesc->addr &= ~(EMACRXD_ADDR_OWNER);
+
+#warning FIXME!!!
+#ifdef HAVE_CLEAN_DCACHE_RANGE
+ /* Flush the modified RX descriptor to RAM */
+
+ arch_clean_dcache((uintptr_t)rxdesc,
+ (uintptr_t)rxdesc +
+ sizeof(struct emac_rxdesc_s));
+#endif
+
+ /* Increment the RX index */
+
+ if (++priv->rxndx >= priv->attr->nrxbuffers)
+ {
+ priv->rxndx = 0;
+ }
+ }
+
+ /* Reset the packet data pointer and packet length */
+
+ dest = dev->d_buf;
+ pktlen = 0;
+
+ /* Start to gather buffers into the packet buffer */
+
+ isframe = true;
+ }
+
+ /* Increment the working index */
+
+ if (++rxndx >= priv->attr->nrxbuffers)
+ {
+ rxndx = 0;
+ }
+
+ /* Copy data into the packet buffer */
+
+ if (isframe)
+ {
+ if (rxndx == priv->rxndx)
+ {
+ nllvdbg("ERROR: No EOF (Invalid of buffers too small)\n");
+ do
+ {
+ /* Give ownership back to the EMAC */
+
+ rxdesc = &priv->rxdesc[priv->rxndx];
+ rxdesc->addr &= ~(EMACRXD_ADDR_OWNER);
+
+#warning FIXME!!!
+#ifdef HAVE_CLEAN_DCACHE_RANGE
+ /* Flush the modified RX descriptor to RAM */
+
+ arch_clean_dcache((uintptr_t)rxdesc,
+ (uintptr_t)rxdesc +
+ sizeof(struct emac_rxdesc_s));
+#endif
+
+ /* Increment the RX index */
+
+ if (++priv->rxndx >= priv->attr->nrxbuffers)
+ {
+ priv->rxndx = 0;
+ }
+ }
+ while (rxndx != priv->rxndx);
+ return -EIO;
+ }
+
+ /* Get the number of bytes to copy from the buffer */
+
+ copylen = EMAC_RX_UNITSIZE;
+ if ((pktlen + copylen) > CONFIG_NET_ETH_MTU)
+ {
+ copylen = CONFIG_NET_ETH_MTU - pktlen;
+ }
+
+ /* Get the data source. Invalidate the source memory region to
+ * force reload from RAM.
+ */
+
+ src = (const uint8_t *)(rxdesc->addr & EMACRXD_ADDR_MASK);
+
+#warning FIXME!!!
+#ifdef HAVE_INVALIDATE_DCACHE_RANGE /* Revisit */
+ arch_invalidate_dcache((uintptr_t)src, (uintptr_t)src + copylen);
+#endif
+
+ /* And do the copy */
+
+ memcpy(dest, src, copylen);
+ dest += copylen;
+ pktlen += copylen;
+
+ /* If the end of frame has been received, return the data */
+
+ if ((rxdesc->status & EMACRXD_STA_EOF) != 0)
+ {
+ /* Frame size from the EMAC */
+
+ dev->d_len = (rxdesc->status & EMACRXD_STA_FRLEN_MASK);
+ nllvdbg("packet %d-%d (%d)\n", priv->rxndx, rxndx, dev->d_len);
+
+ /* All data have been copied in the application frame buffer,
+ * release the RX descriptor
+ */
+
+ while (priv->rxndx != rxndx)
+ {
+ /* Give ownership back to the EMAC */
+
+ rxdesc = &priv->rxdesc[priv->rxndx];
+ rxdesc->addr &= ~(EMACRXD_ADDR_OWNER);
+
+#warning FIXME!!!
+#ifdef HAVE_CLEAN_DCACHE_RANGE
+ /* Flush the modified RX descriptor to RAM */
+
+ arch_clean_dcache((uintptr_t)rxdesc,
+ (uintptr_t)rxdesc +
+ sizeof(struct emac_rxdesc_s));
+#endif
+
+ /* Increment the RX index */
+
+ if (++priv->rxndx >= priv->attr->nrxbuffers)
+ {
+ priv->rxndx = 0;
+ }
+ }
+
+ /* Check if the device packet buffer was large enough to accept
+ * all of the data.
+ */
+
+ nllvdbg("rxndx: %d d_len: %d\n", priv->rxndx, dev->d_len);
+ if (pktlen < dev->d_len)
+ {
+ nlldbg("ERROR: Buffer size %d; frame size %d\n",
+ dev->d_len, pktlen);
+ return -E2BIG;
+ }
+
+ return OK;
+ }
+ }
+
+ /* We have not encountered the SOF yet... discard this fragment and
+ * keep looking
+ */
+
+ else
+ {
+ /* Give ownership back to the EMAC */
+
+ rxdesc->addr &= ~(EMACRXD_ADDR_OWNER);
+
+#warning FIXME!!!
+#ifdef HAVE_CLEAN_DCACHE_RANGE
+ /* Flush the modified RX descriptor to RAM */
+
+ arch_clean_dcache((uintptr_t)rxdesc,
+ (uintptr_t)rxdesc +
+ sizeof(struct emac_rxdesc_s));
+#endif
+ priv->rxndx = rxndx;
+ }
+
+ /* Process the next buffer */
+
+ rxdesc = &priv->rxdesc[rxndx];
+
+#warning FIXME!!!
+#ifdef HAVE_INVALIDATE_DCACHE_RANGE /* Revisit */
+ /* Invalidate the RX descriptor to force re-fetching from RAM */
+
+ arch_invalidate_dcache((uintptr_t)rxdesc,
+ (uintptr_t)rxdesc + sizeof(struct emac_rxdesc_s));
+#endif
+ }
+
+ /* No packet was found */
+
+ priv->rxndx = rxndx;
+ nllvdbg("rxndx: %d\n", priv->rxndx);
+ return -EAGAIN;
+}
+
+/****************************************************************************
+ * Function: sam_receive
+ *
+ * Description:
+ * An interrupt was received indicating the availability of one or more
+ * new RX packets in FIFO memory.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void sam_receive(struct sam_emac_s *priv)
+{
+ struct net_driver_s *dev = &priv->dev;
+
+ /* Loop while while sam_recvframe() successfully retrieves valid
+ * EMAC frames.
+ */
+
+ while (sam_recvframe(priv) == OK)
+ {
+ sam_dumppacket("Received packet", dev->d_buf, dev->d_len);
+
+ /* Check if the packet is a valid size for the uIP buffer configuration
+ * (this should not happen)
+ */
+
+ if (dev->d_len > CONFIG_NET_ETH_MTU)
+ {
+ nlldbg("DROPPED: Too big: %d\n", dev->d_len);
+ continue;
+ }
+
+#ifdef CONFIG_NET_PKT
+ /* When packet sockets are enabled, feed the frame into the packet tap */
+
+ pkt_input(&priv->dev);
+#endif
+
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv4
+ if (BUF->type == HTONS(ETHTYPE_IP))
+ {
+ nllvdbg("IPv4 frame\n");
+
+ /* Handle ARP on input then give the IPv4 packet to the network
+ * layer
+ */
+
+ arp_ipin(&priv->dev);
+ ipv4_input(&priv->dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ /* Update the Ethernet header with the correct MAC address */
+
+#ifdef CONFIG_NET_IPv6
+ if (IFF_IS_IPv4(priv->dev.d_flags))
+#endif
+ {
+ arp_out(&priv->dev);
+ }
+#ifdef CONFIG_NET_IPv6
+ else
+ {
+ neighbor_out(&priv->dev);
+ }
+#endif
+
+ /* And send the packet */
+
+ sam_transmit(priv);
+ }
+ }
+ else
+#endif
+#ifdef CONFIG_NET_IPv6
+ if (BUF->type == HTONS(ETHTYPE_IP6))
+ {
+ nllvdbg("Iv6 frame\n");
+
+ /* Give the IPv6 packet to the network layer */
+
+ ipv6_input(&priv->dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ /* Update the Ethernet header with the correct MAC address */
+
+#ifdef CONFIG_NET_IPv4
+ if (IFF_IS_IPv4(priv->dev.d_flags))
+ {
+ arp_out(&priv->dev);
+ }
+ else
+#endif
+#ifdef CONFIG_NET_IPv6
+ {
+ neighbor_out(&priv->dev);
+ }
+#endif
+
+ /* And send the packet */
+
+ sam_transmit(priv);
+ }
+ }
+ else
+#endif
+#ifdef CONFIG_NET_ARP
+ if (BUF->type == htons(ETHTYPE_ARP))
+ {
+ nllvdbg("ARP frame\n");
+
+ /* Handle ARP packet */
+
+ arp_arpin(&priv->dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ sam_transmit(priv);
+ }
+ }
+ else
+#endif
+ {
+ nlldbg("DROPPED: Unknown type: %04x\n", BUF->type);
+ }
+ }
+}
+
+/****************************************************************************
+ * Function: sam_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that one or more frames have
+ * completed transmission.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void sam_txdone(struct sam_emac_s *priv)
+{
+ struct emac_txdesc_s *txdesc;
+#ifndef __NO_KLUDGES__
+ int ntxdone = 0;
+#endif
+
+ /* Are there any outstanding transmissions? Loop until either (1) all of
+ * the TX descriptors have been examined, or (2) until we encounter the
+ * first descriptor that is still in use by the hardware.
+ */
+
+ while (priv->txhead != priv->txtail)
+ {
+ /* Yes.. check the next buffer at the tail of the list */
+
+ txdesc = &priv->txdesc[priv->txtail];
+
+#warning FIXME!!!
+#ifdef HAVE_INVALIDATE_DCACHE_RANGE /* Revisit */
+ arch_invalidate_dcache((uintptr_t)txdesc,
+ (uintptr_t)txdesc + sizeof(struct emac_txdesc_s));
+#endif
+
+ /* Is this TX descriptor still in use? */
+
+#ifndef __NO_KLUDGES__
+# warning REVISIT
+ /* I have seen cases where we receive interrupts, but the USED
+ * bit is never set in the TX descriptor. This logic assumes
+ * that if we got the interrupt, then there most be at least
+ * one packet that completed. This is not necessarily a safe
+ * assumption.
+ */
+
+ ntxdone++;
+ if ((txdesc->status & EMACTXD_STA_USED) == 0 && ntxdone > 1)
+#else
+ if ((txdesc->status & EMACTXD_STA_USED) == 0)
+#endif
+ {
+ /* Yes.. the descriptor is still in use. However, I have seen a
+ * case (only repeatable on start-up) where the USED bit is never
+ * set. Yikes! If we have encountered the first still busy
+ * descriptor, then we should also have TQBD equal to the descriptor
+ * address. If it is not, then treat is as used anyway.
+ */
+
+#if 0 /* The issue does not exist in the current configuration, but may return */
+#warning REVISIT
+ if (priv->txtail == 0 &&
+ sam_physramaddr((uintptr_t)txdesc) != sam_getreg(priv, SAM_EMAC_TBQB_OFFSET))
+ {
+ txdesc->status = (uint32_t)EMACTXD_STA_USED;
+ arch_clean_dcache((uintptr_t)txdesc,
+ (uintptr_t)txdesc + sizeof(struct emac_txdesc_s));
+ }
+ else
+#endif
+ {
+ /* Otherwise, the descriptor is truly in use. Break out of the
+ * loop now.
+ */
+
+ break;
+ }
+ }
+
+#ifndef __NO_KLUDGES__
+ /* Make sure that the USED bit is set */
+
+ txdesc->status = (uint32_t)EMACTXD_STA_USED;
+#warning FIXME!!!
+#ifdef HAVE_CLEAN_DCACHE_RANGE
+ arch_clean_dcache((uintptr_t)txdesc,
+ (uintptr_t)txdesc + sizeof(struct emac_txdesc_s));
+#endif
+#endif
+
+ /* Increment the tail index */
+
+ if (++priv->txtail >= priv->attr->ntxbuffers)
+ {
+ /* Wrap to the beginning of the TX descriptor list */
+
+ priv->txtail = 0;
+ }
+
+ /* At least one TX descriptor is available. Re-enable RX interrupts.
+ * RX interrupts may previously have been disabled when we ran out of
+ * TX descriptors (see comments in sam_transmit()).
+ */
+
+ sam_putreg(priv, SAM_EMAC_IER_OFFSET, EMAC_INT_RCOMP);
+ }
+
+ /* Then poll uIP for new XMIT data */
+
+ sam_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: sam_interrupt_process
+ *
+ * Description:
+ * Interrupt processing. This may be performed either within the interrupt
+ * handler or on the worker thread, depending upon the configuration
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+static inline void sam_interrupt_process(FAR struct sam_emac_s *priv)
+{
+ uint32_t isr;
+ uint32_t rsr;
+ uint32_t tsr;
+ uint32_t imr;
+ uint32_t regval;
+ uint32_t pending;
+ uint32_t clrbits;
+
+ isr = sam_getreg(priv, SAM_EMAC_ISR_OFFSET);
+ rsr = sam_getreg(priv, SAM_EMAC_RSR_OFFSET);
+ tsr = sam_getreg(priv, SAM_EMAC_TSR_OFFSET);
+ imr = sam_getreg(priv, SAM_EMAC_IMR_OFFSET);
+
+ pending = isr & ~(imr | EMAC_INT_UNUSED);
+ nllvdbg("isr: %08x pending: %08x\n", isr, pending);
+
+ /* Check for the completion of a transmission. This should be done before
+ * checking for received data (because receiving can cause another transmission
+ * before we had a chance to handle the last one).
+ *
+ * ISR:TCOMP is set when a frame has been transmitted. Cleared on read.
+ * TSR:TXCOMP is set when a frame has been transmitted. Cleared by writing a
+ * one to this bit.
+ */
+
+ if ((pending & EMAC_INT_TCOMP) != 0 || (tsr & EMAC_TSR_TXCOMP) != 0)
+ {
+ /* A frame has been transmitted */
+
+ clrbits = EMAC_TSR_TXCOMP;
+
+ /* Check for Retry Limit Exceeded (RLE) */
+
+ if ((tsr & EMAC_TSR_RLE) != 0)
+ {
+ /* Status RLE & Number of discarded buffers */
+
+ clrbits = EMAC_TSR_RLE | sam_txinuse(priv);
+ sam_txreset(priv);
+
+ nlldbg("ERROR: Retry Limit Exceeded TSR: %08x\n", tsr);
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval |= EMAC_NCR_TXEN;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+ }
+
+ /* Check Collision Occurred (COL) */
+
+ if ((tsr & EMAC_TSR_COL) != 0)
+ {
+ nlldbg("ERROR: Collision occurred TSR: %08x\n", tsr);
+ clrbits |= EMAC_TSR_COL;
+ }
+
+ /* Check Transmit Frame Corruption due to AHB error (TFC) */
+
+ if ((tsr & EMAC_TSR_TFC) != 0)
+ {
+ nlldbg("ERROR: Transmit Frame Corruption due to AHB error: %08x\n", tsr);
+ clrbits |= EMAC_TSR_TFC;
+ }
+
+ /* Clear status */
+
+ sam_putreg(priv, SAM_EMAC_TSR_OFFSET, clrbits);
+
+ /* And handle the TX done event */
+
+ sam_txdone(priv);
+ }
+
+ /* Check for the receipt of an RX packet.
+ *
+ * RXCOMP indicates that a packet has been received and stored in memory.
+ * The RXCOMP bit is cleared when the interrupt status register was read.
+ * RSR:REC indicates that one or more frames have been received and placed
+ * in memory. This indication is cleared by writing a one to this bit.
+ */
+
+ if ((pending & EMAC_INT_RCOMP) != 0 || (rsr & EMAC_RSR_REC) != 0)
+ {
+ clrbits = EMAC_RSR_REC;
+
+ /* Check for Receive Overrun.
+ *
+ * RSR:RXOVR will be set if the RX FIFO is not able to store the
+ * receive frame due to a FIFO overflow, or if the receive status
+ * was not taken at the end of the frame. This bit is also set in
+ * DMA packet buffer mode if the packet buffer overflows. For DMA
+ * operation, the buffer will be recovered if an overrun occurs. This
+ * bit is cleared when set to 1.
+ */
+
+ if ((rsr & EMAC_RSR_RXOVR) != 0)
+ {
+ nlldbg("ERROR: Receiver overrun RSR: %08x\n", rsr);
+ clrbits |= EMAC_RSR_RXOVR;
+ }
+
+ /* Check for buffer not available (BNA)
+ *
+ * RSR:BNA means that an attempt was made to get a new buffer and the
+ * pointer indicated that it was owned by the processor. The DMA will
+ * reread the pointer each time an end of frame is received until a
+ * valid pointer is found. This bit is set following each descriptor
+ * read attempt that fails, even if consecutive pointers are
+ * unsuccessful and software has in the mean time cleared the status
+ * flag. Cleared by writing a one to this bit.
+ */
+
+ if ((rsr & EMAC_RSR_BNA) != 0)
+ {
+ nlldbg("ERROR: Buffer not available RSR: %08x\n", rsr);
+ clrbits |= EMAC_RSR_BNA;
+ }
+
+ /* Clear status */
+
+ sam_putreg(priv, SAM_EMAC_RSR_OFFSET, clrbits);
+
+ /* Handle the received packet */
+
+ sam_receive(priv);
+ }
+
+#ifdef CONFIG_DEBUG_NET
+ /* Check for PAUSE Frame received (PFRE).
+ *
+ * ISR:PFRE indicates that a pause frame has been received with non-zero
+ * pause quantum. Cleared on a read.
+ */
+
+ if ((pending & EMAC_INT_PFNZ) != 0)
+ {
+ nlldbg("Pause frame received\n");
+ }
+
+ /* Check for Pause Time Zero (PTZ)
+ *
+ * ISR:PTZ is set Pause Time Zero
+ */
+
+ if ((pending & EMAC_INT_PTZ) != 0)
+ {
+ nlldbg("Pause TO!\n");
+ }
+#endif
+}
+
+/****************************************************************************
+ * Function: sam_interrupt_work
+ *
+ * Description:
+ * Perform interrupt related work from the worker thread
+ *
+ * Parameters:
+ * arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ * Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_NOINTS
+static void sam_interrupt_work(FAR void *arg)
+{
+ FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg;
+ net_lock_t state;
+
+ /* Process pending Ethernet interrupts */
+
+ state = net_lock();
+ sam_interrupt_process(priv);
+ net_unlock(state);
+
+ /* Re-enable Ethernet interrupts */
+
+ up_enable_irq(priv->attr->irq);
+}
+#endif
+
+/****************************************************************************
+ * Function: sam_emac_interrupt
+ *
+ * Description:
+ * Common hardware interrupt handler
+ *
+ * Parameters:
+ * priv - Reference to the EMAC private state structure
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int sam_emac_interrupt(struct sam_emac_s *priv)
+{
+#ifdef CONFIG_NET_NOINTS
+ uint32_t tsr;
+
+ /* 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(priv->attr->irq);
+
+ /* Check for the completion of a transmission. Careful:
+ *
+ * ISR:TCOMP is set when a frame has been transmitted. Cleared on read (so
+ * we cannot read it here).
+ * TSR:TXCOMP is set when a frame has been transmitted. Cleared by writing a
+ * one to this bit.
+ */
+
+ tsr = sam_getreg(priv, SAM_EMAC_TSR_OFFSET);
+ if ((tsr & EMAC_TSR_TXCOMP) != 0)
+ {
+ /* If a TX transfer just completed, then cancel the TX timeout so
+ * there will be do race condition between any subsequent timeout
+ * expiration and the deferred interrupt processing.
+ */
+
+ wd_cancel(priv->txtimeout);
+ }
+
+ /* Cancel any pending poll work */
+
+ work_cancel(HPWORK, &priv->work);
+
+ /* Schedule to perform the interrupt processing on the worker thread. */
+
+ work_queue(HPWORK, &priv->work, sam_interrupt_work, priv, 0);
+
+#else
+ /* Process the interrupt now */
+
+ sam_interrupt_process(priv);
+#endif
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: sam_emac0/1_interrupt
+ *
+ * Description:
+ * EMAC hardware interrupt handler
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMV7_EMAC0
+static int sam_emac0_interrupt(int irq, void *context)
+{
+ return sam_emac_interrupt(&g_emac0);
+}
+#endif
+
+#ifdef CONFIG_SAMV7_EMAC1
+static int sam_emac1_interrupt(int irq, void *context)
+{
+ return sam_emac_interrupt(&g_emac1);
+}
+#endif
+
+/****************************************************************************
+ * Function: sam_txtimeout_process
+ *
+ * Description:
+ * Process a TX timeout. Called from the either the watchdog timer
+ * expiration logic or from the worker thread, depending upon the
+ * configuration. The timeout means that the last TX never completed.
+ * Reset the hardware and start again.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static inline void sam_txtimeout_process(FAR struct sam_emac_s *priv)
+{
+ nlldbg("Timeout!\n");
+
+ /* Reset the hardware. Just take the interface down, then back up again. */
+
+ sam_ifdown(&priv->dev);
+ sam_ifup(&priv->dev);
+
+ /* Then poll uIP for new XMIT data */
+
+ sam_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: sam_txtimeout_work
+ *
+ * Description:
+ * Perform TX timeout related work from the worker thread
+ *
+ * Parameters:
+ * arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ * Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_NOINTS
+static void sam_txtimeout_work(FAR void *arg)
+{
+ FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg;
+ net_lock_t state;
+
+ /* Process pending Ethernet interrupts */
+
+ state = net_lock();
+ sam_txtimeout_process(priv);
+ net_unlock(state);
+}
+#endif
+
+/****************************************************************************
+ * Function: sam_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.
+ *
+ * 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 sam_txtimeout_expiry(int argc, uint32_t arg, ...)
+{
+ FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg;
+
+#ifdef CONFIG_NET_NOINTS
+ /* 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(priv->attr->irq);
+
+ /* Cancel any pending poll or interrupt work. This will have no effect
+ * on work that has already been started.
+ */
+
+ work_cancel(HPWORK, &priv->work);
+
+ /* Schedule to perform the TX timeout processing on the worker thread. */
+
+ work_queue(HPWORK, &priv->work, sam_txtimeout_work, priv, 0);
+#else
+ /* Process the timeout now */
+
+ sam_txtimeout_process(priv);
+#endif
+}
+
+/****************************************************************************
+ * Function: sam_poll_process
+ *
+ * Description:
+ * Perform the periodic poll. This may be called either from watchdog
+ * timer logic or from the worker thread, depending upon the configuration.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline void sam_poll_process(FAR struct sam_emac_s *priv)
+{
+ struct net_driver_s *dev = &priv->dev;
+
+ /* Check if the there are any free TX descriptors. We cannot perform the
+ * TX poll if we do not have buffering for another packet.
+ */
+
+ if (sam_txfree(priv) > 0)
+ {
+ /* Update TCP timing states and poll uIP for new XMIT data. */
+
+ (void)devif_timer(dev, sam_txpoll, SAM_POLLHSEC);
+ }
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_poll_expiry, 1, priv);
+}
+
+/****************************************************************************
+ * Function: sam_poll_work
+ *
+ * Description:
+ * Perform periodic polling from the worker thread
+ *
+ * Parameters:
+ * arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ * Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_NOINTS
+static void sam_poll_work(FAR void *arg)
+{
+ FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg;
+ net_lock_t state;
+
+ /* Perform the poll */
+
+ state = net_lock();
+ sam_poll_process(priv);
+ net_unlock(state);
+}
+#endif
+
+/****************************************************************************
+ * Function: sam_poll_expiry
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void sam_poll_expiry(int argc, uint32_t arg, ...)
+{
+ FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg;
+
+#ifdef CONFIG_NET_NOINTS
+ /* Is our single work structure available? It may not be if there are
+ * pending interrupt actions.
+ */
+
+ if (work_available(&priv->work))
+ {
+ /* Schedule to perform the interrupt processing on the worker thread. */
+
+ work_queue(HPWORK, &priv->work, sam_poll_work, priv, 0);
+ }
+ else
+ {
+ /* No.. Just re-start the watchdog poll timer, missing one polling
+ * cycle.
+ */
+
+ (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_poll_expiry, 1, arg);
+ }
+
+#else
+ /* Process the interrupt now */
+
+ sam_poll_process(priv);
+#endif
+}
+
+/****************************************************************************
+ * Function: sam_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the EMAC interface when an IP address is
+ * provided
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int sam_ifup(struct net_driver_s *dev)
+{
+ struct sam_emac_s *priv = (struct sam_emac_s *)dev->d_private;
+ int ret;
+
+#ifdef CONFIG_NET_IPv4
+ ndbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+ ndbg("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
+ dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
+ dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
+#endif
+
+ /* Configure the EMAC interface for normal operation. */
+
+ nllvdbg("Initialize the EMAC\n");
+ sam_emac_configure(priv);
+
+ /* Set the MAC address (should have been configured while we were down) */
+
+ sam_macaddress(priv);
+
+#ifdef CONFIG_NET_ICMPv6
+ /* Set up IPv6 multicast address filtering */
+
+ sam_ipv6multicast(priv);
+#endif
+
+ /* Initialize for PHY access */
+
+ ret = sam_phyinit(priv);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: sam_phyinit failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Auto Negotiate, working in RMII mode */
+
+ ret = sam_autonegotiate(priv);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: sam_autonegotiate failed: %d\n", ret);
+ return ret;
+ }
+
+ while (sam_linkup(priv) == 0);
+ nllvdbg("Link detected \n");
+
+ /* Enable normal MAC operation */
+
+ nllvdbg("Enable normal operation\n");
+
+ /* Set and activate a timer process */
+
+ (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_poll_expiry, 1, (uint32_t)priv);
+
+ /* Enable the EMAC interrupt */
+
+ priv->ifup = true;
+ up_enable_irq(priv->attr->irq);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: sam_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int sam_ifdown(struct net_driver_s *dev)
+{
+ struct sam_emac_s *priv = (struct sam_emac_s *)dev->d_private;
+ irqstate_t flags;
+
+ nlldbg("Taking the network down\n");
+
+ /* Disable the EMAC interrupt */
+
+ flags = irqsave();
+ up_disable_irq(priv->attr->irq);
+
+ /* 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 sam_ifup() always
+ * successfully brings the interface back up.
+ */
+
+ sam_emac_reset(priv);
+
+ /* Mark the device "down" */
+
+ priv->ifup = false;
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: sam_txavail_process
+ *
+ * Description:
+ * Perform an out-of-cycle poll.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static inline void sam_txavail_process(FAR struct sam_emac_s *priv)
+{
+ nllvdbg("ifup: %d\n", priv->ifup);
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (priv->ifup)
+ {
+ /* Poll uIP for new XMIT data */
+
+ sam_dopoll(priv);
+ }
+}
+
+/****************************************************************************
+ * Function: sam_txavail_work
+ *
+ * Description:
+ * Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ * arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_NOINTS
+static void sam_txavail_work(FAR void *arg)
+{
+ FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)arg;
+ net_lock_t state;
+
+ /* Perform the poll */
+
+ state = net_lock();
+ sam_txavail_process(priv);
+ net_unlock(state);
+}
+#endif
+
+/****************************************************************************
+ * Function: sam_txavail
+ *
+ * Description:
+ * Driver callback invoked when new TX data is available. This is a
+ * stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ * latency.
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int sam_txavail(struct net_driver_s *dev)
+{
+ FAR struct sam_emac_s *priv = (FAR struct sam_emac_s *)dev->d_private;
+
+#ifdef CONFIG_NET_NOINTS
+ /* 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->work))
+ {
+ /* Schedule to serialize the poll on the worker thread. */
+
+ work_queue(HPWORK, &priv->work, sam_txavail_work, priv, 0);
+ }
+
+#else
+ irqstate_t flags;
+
+ /* Disable interrupts because this function may be called from interrupt
+ * level processing.
+ */
+
+ flags = irqsave();
+
+ /* Perform the out-of-cycle poll now */
+
+ sam_txavail_process(priv);
+ irqrestore(flags);
+#endif
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: sam_hashindx
+ *
+ * Description:
+ * Cacuclate the hash address register index. The hash address register
+ * is 64 bits long and takes up two locations in the memory map. The
+ * destination address is reduced to a 6-bit index into the 64-bit Hash
+ * Register using the following hash function: The hash function is an XOR
+ * of every sixth bit of the destination address.
+ *
+ * ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47
+ * ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46
+ * ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45
+ * ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44
+ * ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43
+ * ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42
+ *
+ * Where da:00 represents the least significant bit of the first byte
+ * received and da:47 represents the most significant bit of the last byte
+ * received.
+ *
+ * Input Parameters:
+ * mac - The multicast address to be hashed
+ *
+ * Returned Value:
+ * The 6-bit hash table index
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6)
+static unsigned int sam_hashindx(const uint8_t *mac)
+{
+ unsigned int ndx;
+
+ /* Isolate: mac[0]
+ * ... 05 04 03 02 01 00] */
+
+ ndx = mac[0];
+
+ /* Isolate: mac[1] mac[0]
+ * ...11 10 09 08] [07 06 ...
+ *
+ * Accumulate: 05 04 03 02 01 00
+ * XOR: 11 10 09 08 07 06
+ */
+
+ ndx ^= (mac[1] << 2) | (mac[0] >> 6);
+
+ /* Isolate: mac[2] mac[1]
+ * ... 17 16] [15 14 13 12 ...
+ *
+ * Accumulate: 05 04 03 02 01 00
+ * XOR: 11 10 09 08 07 06
+ * XOR: 17 16 15 14 13 12
+ */
+
+ ndx ^= (mac[2] << 4) | (mac[1] >> 4);
+
+ /* Isolate: mac[2]
+ * [23 22 21 20 19 18 ...
+ *
+ * Accumulate: 05 04 03 02 01 00
+ * XOR: 11 10 09 08 07 06
+ * XOR: 17 16 15 14 13 12
+ * XOR: 23 22 21 20 19 18
+ */
+
+ ndx ^= (mac[2] >> 2);
+
+ /* Isolate: mac[3]
+ * ... 29 28 27 26 25 24]
+ *
+ * Accumulate: 05 04 03 02 01 00
+ * XOR: 11 10 09 08 07 06
+ * XOR: 17 16 15 14 13 12
+ * XOR: 23 22 21 20 19 18
+ * XOR: 29 28 27 26 25 24
+ */
+
+ ndx ^= mac[3];
+
+ /* Isolate: mac[4] mac[3]
+ * ... 35 34 33 32] [31 30 ...
+ *
+ * Accumulate: 05 04 03 02 01 00
+ * XOR: 11 10 09 08 07 06
+ * XOR: 17 16 15 14 13 12
+ * XOR: 23 22 21 20 19 18
+ * XOR: 29 28 27 26 25 24
+ * XOR: 35 34 33 32 31 30
+ */
+
+ ndx ^= (mac[4] << 2) | (mac[3] >> 6);
+
+ /* Isolate: mac[5] mac[4]
+ * ... 41 40] [39 38 37 36 ...
+ *
+ * Accumulate: 05 04 03 02 01 00
+ * XOR: 11 10 09 08 07 06
+ * XOR: 17 16 15 14 13 12
+ * XOR: 23 22 21 20 19 18
+ * XOR: 29 28 27 26 25 24
+ * XOR: 35 34 33 32 31 30
+ * XOR: 41 40 39 38 37 36
+ */
+
+ ndx ^= (mac[5] << 4) | (mac[4] >> 4);
+
+ /* Isolate: mac[5]
+ * [47 46 45 44 43 42 ...
+ *
+ * Accumulate: 05 04 03 02 01 00
+ * XOR: 11 10 09 08 07 06
+ * XOR: 17 16 15 14 13 12
+ * XOR: 23 22 21 20 19 18
+ * XOR: 29 28 27 26 25 24
+ * XOR: 35 34 33 32 31 30
+ * XOR: 41 40 39 38 37 36
+ * XOR: 47 46 45 44 43 42
+ */
+
+ ndx ^= (mac[5] >> 2);
+
+ /* Mask out the garbage bits and return the 6-bit index */
+
+ return ndx & 0x3f;
+}
+#endif /* CONFIG_NET_IGMP || CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: sam_addmac
+ *
+ * Description:
+ * NuttX Callback: Add the specified MAC address to the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be added
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6)
+static int sam_addmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+ struct sam_emac_s *priv = (struct sam_emac_s *)dev->d_private;
+ uint32_t regval;
+ unsigned int regoffset;
+ unsigned int ndx;
+ unsigned int bit;
+
+ nllvdbg("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+ /* Calculate the 6-bit has table index */
+
+ ndx = sam_hashindx(mac);
+
+ /* Add the multicast address to the hardware multicast hash table */
+
+ if (ndx >= 32)
+ {
+ regoffset = SAM_EMAC_HRT_OFFSET; /* Hash Register Top [63:32] Register */
+ bit = 1 << (ndx - 32); /* Bit 0-31 */
+ }
+ else
+ {
+ regoffset = SAM_EMAC_HRB_OFFSET; /* Hash Register Bottom [31:0] Register */
+ bit = 1 << ndx; /* Bit 0-31 */
+ }
+
+ regval = sam_getreg(priv, regoffset);
+ regval |= bit;
+ sam_putreg(priv, regoffset, regval);
+
+ /* The unicast hash enable and the multicast hash enable bits in the
+ * Network Configuration Register enable the reception of hash matched
+ * frames:
+ *
+ * - A multicast match will be signalled if the multicast hash enable bit
+ * is set, da:00 is logic 1 and the hash index points to a bit set in
+ * the Hash Register.
+ * - A unicast match will be signalled if the unicast hash enable bit is
+ * set, da:00 is logic 0 and the hash index points to a bit set in the
+ * Hash Register.
+ */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCFGR_OFFSET);
+ regval &= ~EMAC_NCFGR_UNIHEN; /* Disable unicast matching */
+ regval |= EMAC_NCFGR_MTIHEN; /* Enable multicast matching */
+ sam_putreg(priv, SAM_EMAC_NCFGR_OFFSET, regval);
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: sam_rmmac
+ *
+ * Description:
+ * NuttX Callback: Remove the specified MAC address from the hardware multicast
+ * address filtering
+ *
+ * Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * mac - The MAC address to be removed
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int sam_rmmac(struct net_driver_s *dev, const uint8_t *mac)
+{
+ struct sam_emac_s *priv = (struct sam_emac_s *)dev->d_private;
+ uint32_t regval;
+ unsigned int regoffset1;
+ unsigned int regoffset2;
+ unsigned int ndx;
+ unsigned int bit;
+
+ nllvdbg("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+ /* Calculate the 6-bit has table index */
+
+ ndx = sam_hashindx(mac);
+
+ /* Remove the multicast address to the hardware multicast hast table */
+
+ if (ndx >= 32)
+ {
+ regoffset1 = SAM_EMAC_HRT_OFFSET; /* Hash Register Top [63:32] Register */
+ regoffset2 = SAM_EMAC_HRB_OFFSET; /* Hash Register Bottom [31:0] Register */
+ bit = 1 << (ndx - 32); /* Bit 0-31 */
+ }
+ else
+ {
+ regoffset1 = SAM_EMAC_HRB_OFFSET; /* Hash Register Bottom [31:0] Register */
+ regoffset2 = SAM_EMAC_HRT_OFFSET; /* Hash Register Top [63:32] Register */
+ bit = 1 << ndx; /* Bit 0-31 */
+ }
+
+ regval = sam_getreg(priv, regoffset1);
+ regval &= ~bit;
+ sam_putreg(priv, regoffset1, regval);
+
+ /* The unicast hash enable and the multicast hash enable bits in the
+ * Network Configuration Register enable the reception of hash matched
+ * frames:
+ *
+ * - A multicast match will be signalled if the multicast hash enable bit
+ * is set, da:00 is logic 1 and the hash index points to a bit set in
+ * the Hash Register.
+ * - A unicast match will be signalled if the unicast hash enable bit is
+ * set, da:00 is logic 0 and the hash index points to a bit set in the
+ * Hash Register.
+ */
+
+ /* Are all multicast address matches disabled? */
+
+ if (regval == 0 && sam_getreg(priv, regoffset2) == 0)
+ {
+ /* Yes.. disable all address matching */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCFGR_OFFSET);
+ regval &= ~(EMAC_NCFGR_UNIHEN | EMAC_NCFGR_MTIHEN);
+ sam_putreg(priv, SAM_EMAC_NCFGR_OFFSET, regval);
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: sam_ioctl
+ *
+ * Description:
+ * Handles driver ioctl calls:
+ *
+ * SIOCMIINOTIFY - Set up to received notifications from PHY interrupting
+ * events.
+ *
+ * SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG:
+ * Executes the SIOCxMIIxxx command and responds using the request struct
+ * that must be provided as its 2nd parameter.
+ *
+ * When called with SIOCGMIIPHY it will get the PHY address for the device
+ * and write it to the req->phy_id field of the request struct.
+ *
+ * When called with SIOCGMIIREG it will read a register of the PHY that is
+ * specified using the req->reg_no struct field and then write its output
+ * to the req->val_out field.
+ *
+ * When called with SIOCSMIIREG it will write to a register of the PHY that
+ * is specified using the req->reg_no struct field and use req->val_in as
+ * its input.
+ *
+ * Parameters:
+ * dev - Ethernet device structure
+ * cmd - SIOCxMIIxxx command code
+ * arg - Request structure also used to return values
+ *
+ * Returned Value: Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+static int sam_ioctl(struct net_driver_s *dev, int cmd, long arg)
+{
+ struct sam_emac_s *priv = (struct sam_emac_s *)dev->d_private;
+ int ret;
+
+ switch (cmd)
+ {
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+ case SIOCMIINOTIFY: /* Set up for PHY event notifications */
+ {
+ struct mii_iotcl_notify_s *req = (struct mii_iotcl_notify_s *)((uintptr_t)arg);
+
+ ret = phy_notify_subscribe(dev->d_ifname, req->pid, req->signo, req->arg);
+ if (ret == OK)
+ {
+ /* Enable PHY link up/down interrupts */
+
+ ret = sam_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);
+ uint32_t regval;
+
+ /* Enable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval | EMAC_NCR_MPE);
+
+ /* Read from the requested register */
+
+ ret = sam_phyread(priv, req->phy_id, req->reg_num, &req->val_out);
+
+ /* Disable management port (probably) */
+
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+ }
+ break;
+
+ case SIOCSMIIREG: /* Set register in MII PHY */
+ {
+ struct mii_ioctl_data_s *req = (struct mii_ioctl_data_s *)((uintptr_t)arg);
+ uint32_t regval;
+
+ /* Enable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval | EMAC_NCR_MPE);
+
+ /* Write to the requested register */
+
+ ret = sam_phywrite(priv, req->phy_id, req->reg_num, req->val_in);
+
+ /* Disable management port (probably) */
+
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+ }
+ break;
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_NETDEV_PHY_IOCTL */
+
+/****************************************************************************
+ * Function: sam_phydump
+ *
+ * Description:
+ * Dump the contents of PHY registers
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_VERBOSE)
+static void sam_phydump(struct sam_emac_s *priv)
+{
+ uint32_t regval;
+ uint16_t phyval;
+
+ /* Enable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval |= EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+
+ nllvdbg("%s Registers (Address %02x)\n",
+ priv->attr->rmii ? "RMII" : "MII", priv->phyaddr);
+
+ sam_phyread(priv, priv->phyaddr, MII_MCR, &phyval);
+ nllvdbg(" MCR: %04x\n", phyval);
+ sam_phyread(priv, priv->phyaddr, MII_MSR, &phyval);
+ nllvdbg(" MSR: %04x\n", phyval);
+ sam_phyread(priv, priv->phyaddr, MII_ADVERTISE, &phyval);
+ nllvdbg(" ADVERTISE: %04x\n", phyval);
+ sam_phyread(priv, priv->phyaddr, MII_LPA, &phyval);
+ nllvdbg(" LPR: %04x\n", phyval);
+ sam_phyread(priv, priv->phyaddr, priv->attr->physr, &phyval);
+ nllvdbg(" PHYSR: %04x\n", phyval);
+
+ /* Disable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval &= ~EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+}
+#endif
+
+/****************************************************************************
+ * Function: sam_is*
+ *
+ * Description:
+ * Helpers to simplify decoding PHY status register bits
+ *
+ * Parameters:
+ * physr - The value of the PHY status register
+ *
+ * Returned Value:
+ * True: The PHY configuration is selected; False; some other PHY
+ * configuration is selected.
+ *
+ ****************************************************************************/
+
+#if 0 /* Not used */
+static bool sam_is10hdx(struct sam_emac_s *priv, uint16_t physr)
+{
+ uint16_t mask;
+ uint16_t match;
+
+ if (priv->attr->sralt)
+ {
+ mask = priv->attr->u.alt.altmask;
+ match = priv->attr->u.alt.hdx10;
+ }
+ else
+ {
+ mask = priv->attr->u.std.stdmask;
+ match = 0;
+ }
+
+ return (physr & mask) == match;
+}
+#endif
+
+static bool sam_is100hdx(struct sam_emac_s *priv, uint16_t physr)
+{
+ uint16_t mask;
+ uint16_t match;
+
+ if (priv->attr->sralt)
+ {
+ mask = priv->attr->u.alt.altmask;
+ match = priv->attr->u.alt.hdx100;
+ }
+ else
+ {
+ mask = priv->attr->u.std.stdmask;
+ match = priv->attr->u.std.speed100;
+ }
+
+ return (physr & mask) == match;
+}
+
+static bool sam_is10fdx(struct sam_emac_s *priv, uint16_t physr)
+{
+ uint16_t mask;
+ uint16_t match;
+
+ if (priv->attr->sralt)
+ {
+ mask = priv->attr->u.alt.altmask;
+ match = priv->attr->u.alt.fdx10;
+ }
+ else
+ {
+ mask = priv->attr->u.std.stdmask;
+ match = priv->attr->u.std.fduplex;
+ }
+
+ return (physr & mask) == match;
+}
+
+static bool sam_is100fdx(struct sam_emac_s *priv, uint16_t physr)
+{
+ uint16_t mask;
+ uint16_t match;
+
+ if (priv->attr->sralt)
+ {
+ mask = priv->attr->u.alt.altmask;
+ match = priv->attr->u.alt.fdx100;
+ }
+ else
+ {
+ mask = priv->attr->u.std.stdmask;
+ match = (priv->attr->u.std.fduplex | priv->attr->u.std.speed100);
+ }
+
+ return (physr & mask) == match;
+}
+
+/****************************************************************************
+ * Function: sam_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.
+ *
+ * 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 sam_phyintenable(struct sam_emac_s *priv)
+{
+#if defined(SAMV7_EMAC0_PHY_KSZ8051) || defined(SAMV7_EMAC0_PHY_KSZ8061) || \
+ defined(SAMV7_EMAC0_PHY_KSZ8081) || \
+ defined(SAMV7_EMAC1_PHY_KSZ8051) || defined(SAMV7_EMAC1_PHY_KSZ8061) || \
+ defined(SAMV7_EMAC1_PHY_KSZ8081)
+ uint32_t regval;
+ uint16_t phyval;
+ int ret;
+
+ /* Does this MAC support a KSZ80x1 PHY? */
+
+ if (priv->phytype == SAMV7_PHY_KSZ8051 ||
+ priv->phytype == SAMV7_PHY_KSZ8061 ||
+ priv->phytype == SAMV7_PHY_KSZ8081)
+ {
+ /* Enable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval | EMAC_NCR_MPE);
+
+ /* Read the interrupt status register in order to clear any pending
+ * interrupts
+ */
+
+ ret = sam_phyread(priv, priv->phyaddr, MII_KSZ8081_INT, &phyval);
+ if (ret == OK)
+ {
+ /* Enable link up/down interrupts */
+
+ ret = sam_phywrite(priv, priv->phyaddr, MII_KSZ8081_INT,
+ (MII_KSZ80x1_INT_LDEN | MII_KSZ80x1_INT_LUEN));
+ }
+
+ /* Disable management port (probably) */
+
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+ }
+ else
+#endif
+ {
+ ndbg("ERROR: Unsupported PHY type: %d\n", priv->phytype);
+ ret = -ENOSYS;
+ }
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Function: sam_phywait
+ *
+ * Description:
+ * Wait for the PHY to become IDLE
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; Negated errno (-ETIMEDOUT) on failure.
+ *
+ ****************************************************************************/
+
+static int sam_phywait(struct sam_emac_s *priv)
+{
+ volatile unsigned int retries;
+
+ /* Loop for the configured number of attempts */
+
+ for (retries = 0; retries < PHY_RETRY_MAX; retries++)
+ {
+ /* Is the PHY IDLE */
+
+ if ((sam_getreg(priv, SAM_EMAC_NSR_OFFSET) & EMAC_NSR_IDLE) != 0)
+ {
+ return OK;
+ }
+ }
+
+ return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: sam_phyreset
+ *
+ * Description:
+ * Reset the PHY
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int sam_phyreset(struct sam_emac_s *priv)
+{
+ uint32_t regval;
+ uint16_t mcr;
+ int timeout;
+ int ret;
+
+ nllvdbg(" sam_phyreset\n");
+
+ /* Enable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval |= EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+
+ /* Reset the PHY */
+
+ ret = sam_phywrite(priv, priv->phyaddr, MII_MCR, MII_MCR_RESET);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: sam_phywrite failed: %d\n", ret);
+ }
+
+ /* Wait for the PHY reset to complete */
+
+ ret = -ETIMEDOUT;
+ for (timeout = 0; timeout < 10; timeout++)
+ {
+ mcr = MII_MCR_RESET;
+ int result = sam_phyread(priv, priv->phyaddr, MII_MCR, &mcr);
+ if (result < 0)
+ {
+ nlldbg("ERROR: Failed to read the MCR register: %d\n", ret);
+ ret = result;
+ }
+ else if ((mcr & MII_MCR_RESET) == 0)
+ {
+ ret = OK;
+ break;
+ }
+ }
+
+ /* Disable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval &= ~EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+ return ret;
+}
+
+/****************************************************************************
+ * Function: sam_phyfind
+ *
+ * Description:
+ * Verify the PHY address and, if it is bad, try to one that works.
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int sam_phyfind(struct sam_emac_s *priv, uint8_t *phyaddr)
+{
+ uint32_t regval;
+ uint16_t phyval;
+ uint8_t candidate;
+ unsigned int offset;
+ int ret = -ESRCH;
+
+ nllvdbg("Find a valid PHY address\n");
+
+ /* Enable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval |= EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+
+ candidate = *phyaddr;
+
+ /* Check current candidate address */
+
+ ret = sam_phyread(priv, candidate, MII_PHYID1, &phyval);
+ if (ret == OK && phyval == priv->attr->msoui)
+ {
+ *phyaddr = candidate;
+ ret = OK;
+ }
+
+ /* The current address does not work... try another */
+
+ else
+ {
+ nlldbg("ERROR: sam_phyread failed for PHY address %02x: %d\n",
+ candidate, ret);
+
+ for (offset = 0; offset < 32; offset++)
+ {
+ /* Get the next candidate PHY address */
+
+ candidate = (candidate + 1) & 0x1f;
+
+ /* Try reading the PHY ID from the candidate PHY address */
+
+ ret = sam_phyread(priv, candidate, MII_PHYID1, &phyval);
+ if (ret == OK && phyval == priv->attr->msoui)
+ {
+ ret = OK;
+ break;
+ }
+ }
+ }
+
+ if (ret == OK)
+ {
+ nllvdbg(" PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+ *phyaddr = candidate;
+ sam_phyread(priv, candidate, priv->attr->physr, &phyval);
+ nllvdbg(" PHYSR: %04x PHY addr: %d\n", phyval, candidate);
+ }
+
+ /* Disable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval &= ~EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+ return ret;
+}
+
+/****************************************************************************
+ * Function: sam_phyread
+ *
+ * Description:
+ * Read a PHY register.
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ * phyaddr - The PHY device address
+ * regaddr - The PHY register address
+ * phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int sam_phyread(struct sam_emac_s *priv, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t *phyval)
+{
+ uint32_t regval;
+ int ret;
+
+ /* Make sure that the PHY is idle */
+
+ ret = sam_phywait(priv);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: sam_phywait failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Write the PHY Maintenance register */
+
+ regval = EMAC_MAN_DATA(0) | EMAC_MAN_WTN | EMAC_MAN_REGA(regaddr) |
+ EMAC_MAN_PHYA(phyaddr) | EMAC_MAN_READ | EMAC_MAN_WZO;
+
+ if (!priv->attr->clause45)
+ {
+ /* CLTTO must be set for Clause 22 operation. To read clause 45 PHYs,
+ * bit 30 should be written with a 0 rather than a 1.
+ */
+
+ regval |= EMAC_MAN_CLTTO;
+ }
+
+ sam_putreg(priv, SAM_EMAC_MAN_OFFSET, regval);
+
+ /* Wait until the PHY is again idle */
+
+ ret = sam_phywait(priv);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: sam_phywait failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Return data */
+
+ *phyval = (uint16_t)(sam_getreg(priv, SAM_EMAC_MAN_OFFSET) & EMAC_MAN_DATA_MASK);
+ return OK;
+}
+
+/****************************************************************************
+ * Function: sam_phywrite
+ *
+ * Description:
+ * Write to a PHY register.
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ * phyaddr - The PHY device address
+ * regaddr - The PHY register address
+ * phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int sam_phywrite(struct sam_emac_s *priv, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t phyval)
+{
+ uint32_t regval;
+ int ret;
+
+ /* Make sure that the PHY is idle */
+
+ ret = sam_phywait(priv);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: sam_phywait failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Write the PHY Maintenance register */
+
+ regval = EMAC_MAN_DATA(phyval) | EMAC_MAN_WTN | EMAC_MAN_REGA(regaddr) |
+ EMAC_MAN_PHYA(phyaddr) | EMAC_MAN_WRITE | EMAC_MAN_WZO;
+
+ if (!priv->attr->clause45)
+ {
+ /* CLTTO must be set for Clause 22 operation. To read clause 45 PHYs,
+ * bit 30 should be written with a 0 rather than a 1.
+ */
+
+ regval |= EMAC_MAN_CLTTO;
+ }
+
+ sam_putreg(priv, SAM_EMAC_MAN_OFFSET, regval);
+
+ /* Wait until the PHY is again IDLE */
+
+ ret = sam_phywait(priv);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: sam_phywait failed: %d\n", ret);
+ return ret;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: sam_autonegotiate
+ *
+ * Description:
+ * Autonegotiate speed and duplex.
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int sam_autonegotiate(struct sam_emac_s *priv)
+{
+ uint32_t regval;
+ uint16_t phyid1;
+ uint16_t phyid2;
+ uint16_t mcr;
+ uint16_t msr;
+ uint16_t advertise;
+ uint16_t lpa;
+ int timeout;
+ int ret;
+
+ /* Enable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval |= EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+
+ /* Verify tht we can read the PHYID register */
+
+ ret = sam_phyread(priv, priv->phyaddr, MII_PHYID1, &phyid1);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to read PHYID1\n");
+ goto errout;
+ }
+
+ nllvdbg("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr);
+
+ ret = sam_phyread(priv, priv->phyaddr, MII_PHYID2, &phyid2);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to read PHYID2\n");
+ goto errout;
+ }
+
+ nllvdbg("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr);
+
+ if (phyid1 == priv->attr->msoui &&
+ ((phyid2 & MII_PHYID2_OUI_MASK) >> MII_PHYID2_OUI_SHIFT) ==
+ (uint16_t)priv->attr->lsoui)
+ {
+ nllvdbg(" Vendor Model Number: %04x\n",
+ (phyid2 & MII_PHYID2_MODEL_MASK) >> MII_PHYID2_MODEL_SHIFT);
+ nllvdbg(" Model Revision Number: %04x\n",
+ (phyid2 & MII_PHYID2_REV_MASK) >> MII_PHYID2_REV_SHIFT);
+ }
+ else
+ {
+ nlldbg("ERROR: PHY not recognized\n");
+ }
+
+ /* Setup control register */
+
+ ret = sam_phyread(priv, priv->phyaddr, MII_MCR, &mcr);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to read MCR\n");
+ goto errout;
+ }
+
+ mcr &= ~MII_MCR_ANENABLE; /* Remove autonegotiation enable */
+ mcr &= ~(MII_MCR_LOOPBACK | MII_MCR_PDOWN);
+ mcr |= MII_MCR_ISOLATE; /* Electrically isolate PHY */
+
+ ret = sam_phywrite(priv, priv->phyaddr, MII_MCR, mcr);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to write MCR\n");
+ goto errout;
+ }
+
+ /* Set the Auto_negotiation Advertisement Register MII advertising for
+ * Next page 100BaseTxFD and HD, 10BaseTFD and HD, IEEE 802.3
+ */
+
+ advertise = MII_ADVERTISE_100BASETXFULL | MII_ADVERTISE_100BASETXHALF |
+ MII_ADVERTISE_10BASETXFULL | MII_ADVERTISE_10BASETXHALF |
+ MII_ADVERTISE_8023;
+
+ ret = sam_phywrite(priv, priv->phyaddr, MII_ADVERTISE, advertise);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to write ANAR\n");
+ goto errout;
+ }
+
+ /* Read and modify control register */
+
+ ret = sam_phyread(priv, priv->phyaddr, MII_MCR, &mcr);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to read MCR\n");
+ goto errout;
+ }
+
+ mcr |= (MII_MCR_SPEED100 | MII_MCR_ANENABLE | MII_MCR_FULLDPLX);
+ ret = sam_phywrite(priv, priv->phyaddr, MII_MCR, mcr);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to write MCR\n");
+ goto errout;
+ }
+
+ /* Restart Auto_negotiation */
+
+ mcr |= MII_MCR_ANRESTART;
+ mcr &= ~MII_MCR_ISOLATE;
+
+ ret = sam_phywrite(priv, priv->phyaddr, MII_MCR, mcr);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to write MCR\n");
+ goto errout;
+ }
+
+ nllvdbg(" MCR: %04x\n", mcr);
+
+ /* Check AutoNegotiate complete */
+
+ timeout = 0;
+ for (;;)
+ {
+ ret = sam_phyread(priv, priv->phyaddr, MII_MSR, &msr);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to read MSR\n");
+ goto errout;
+ }
+
+ /* Completed successfully? */
+
+ if ((msr & MII_MSR_ANEGCOMPLETE) != 0)
+ {
+ /* Yes.. break out of the loop */
+
+ nllvdbg("AutoNegotiate complete\n");
+ break;
+ }
+
+ /* No.. check for a timeout */
+
+ if (++timeout >= PHY_RETRY_MAX)
+ {
+ nlldbg("ERROR: TimeOut\n");
+ sam_phydump(priv);
+ ret = -ETIMEDOUT;
+ goto errout;
+ }
+ }
+
+ /* Get the AutoNeg Link partner base page */
+
+ ret = sam_phyread(priv, priv->phyaddr, MII_LPA, &lpa);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to read ANLPAR\n");
+ goto errout;
+ }
+
+ /* Setup the EMAC link speed */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCFGR_OFFSET);
+ regval &= ~(EMAC_NCFGR_SPD | EMAC_NCFGR_FD);
+
+ if (((advertise & lpa) & MII_ADVERTISE_100BASETXFULL) != 0)
+ {
+ /* Set MII for 100BaseTX and Full Duplex */
+
+ regval |= (EMAC_NCFGR_SPD | EMAC_NCFGR_FD);
+ }
+ else if (((advertise & lpa) & MII_ADVERTISE_10BASETXFULL) != 0)
+ {
+ /* Set MII for 10BaseT and Full Duplex */
+
+ regval |= EMAC_NCFGR_FD;
+ }
+ else if (((advertise & lpa) & MII_ADVERTISE_100BASETXHALF) != 0)
+ {
+ /* Set MII for 100BaseTX and half Duplex */
+
+ regval |= EMAC_NCFGR_SPD;
+ }
+#if 0
+ else if (((advertise & lpa) & MII_ADVERTISE_10BASETXHALF) != 0)
+ {
+ /* set MII for 10BaseT and half Duplex */
+ }
+#endif
+
+ sam_putreg(priv, SAM_EMAC_NCFGR_OFFSET, regval);
+
+ /* Select RMII/MII */
+
+ regval = sam_getreg(priv, SAM_EMAC_UR_OFFSET);
+ regval &= ~EMAC_UR_RMII; /* Assume MII */
+
+ if (priv->attr->rmii)
+ {
+ /* Not MII, select RMII */
+
+ regval |= EMAC_UR_RMII;
+ }
+
+ sam_putreg(priv, SAM_EMAC_UR_OFFSET, regval);
+
+errout:
+ /* Disable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval &= ~EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+ return ret;
+}
+
+/****************************************************************************
+ * Function: sam_linkup
+ *
+ * Description:
+ * Check if the link is up
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * true: The link is up
+ *
+ ****************************************************************************/
+
+static bool sam_linkup(struct sam_emac_s *priv)
+{
+ uint32_t regval;
+ uint16_t msr;
+ uint16_t physr;
+ bool linkup = false;
+ int ret;
+
+ /* Enable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval |= EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+
+ ret = sam_phyread(priv, priv->phyaddr, MII_MSR, &msr);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to read MSR: %d\n", ret);
+ goto errout;
+ }
+
+ if ((msr & MII_MSR_LINKSTATUS) == 0)
+ {
+ nlldbg("ERROR: MSR LinkStatus: %04x\n", msr);
+ goto errout;
+ }
+
+ /* Re-configure Link speed */
+
+ ret = sam_phyread(priv, priv->phyaddr, priv->attr->physr, &physr);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to read PHYSR: %d\n", ret);
+ goto errout;
+ }
+
+ regval = sam_getreg(priv, SAM_EMAC_NCFGR_OFFSET);
+ regval &= ~(EMAC_NCFGR_SPD | EMAC_NCFGR_FD);
+
+ if ((msr & MII_MSR_100BASETXFULL) != 0 && sam_is100fdx(priv, physr))
+ {
+ /* Set EMAC for 100BaseTX and Full Duplex */
+
+ regval |= (EMAC_NCFGR_SPD | EMAC_NCFGR_FD);
+ }
+ else if ((msr & MII_MSR_10BASETXFULL) != 0 && sam_is10fdx(priv, physr))
+ {
+ /* Set MII for 10BaseT and Full Duplex */
+
+ regval |= EMAC_NCFGR_FD;
+ }
+
+ else if ((msr & MII_MSR_100BASETXHALF) != 0 && sam_is100hdx(priv, physr))
+ {
+ /* Set MII for 100BaseTX and Half Duplex */
+
+ regval |= EMAC_NCFGR_SPD;
+ }
+
+#if 0
+ else if ((msr & MII_MSR_10BASETXHALF) != 0 && sam_is10hdx(priv, physr))
+ {
+ /* Set MII for 10BaseT and Half Duplex */
+ }
+#endif
+
+ sam_putreg(priv, SAM_EMAC_NCFGR_OFFSET, regval);
+
+ /* Start the EMAC transfers */
+
+ nllvdbg("Link is up\n");
+ linkup = true;
+
+errout:
+ /* Disable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval &= ~EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+
+ return linkup;
+}
+
+/****************************************************************************
+ * Function: sam_phyinit
+ *
+ * Description:
+ * Configure the PHY and determine the link speed/duplex.
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int sam_phyinit(struct sam_emac_s *priv)
+{
+ uint32_t regval;
+ uint32_t mck;
+ int ret;
+
+ /* Configure PHY clocking */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCFGR_OFFSET);
+ regval &= ~EMAC_NCFGR_CLK_MASK;
+
+ mck = BOARD_MCK_FREQUENCY;
+ if (mck > (160*1000*1000))
+ {
+ ndbg("ERROR: Cannot realize PHY clock\n");
+ return -EINVAL;
+ }
+ else if (mck > (80*1000*1000))
+ {
+ regval |= EMAC_NCFGR_CLK_DIV64; /* MCK divided by 64 (MCK up to 160 MHz) */
+ }
+ else if (mck > (40*1000*1000))
+ {
+ regval |= EMAC_NCFGR_CLK_DIV32; /* MCK divided by 32 (MCK up to 80 MHz) */
+ }
+ else if (mck > (20*1000*1000))
+ {
+ regval |= EMAC_NCFGR_CLK_DIV16; /* MCK divided by 16 (MCK up to 40 MHz) */
+ }
+ else
+ {
+ regval |= EMAC_NCFGR_CLK_DIV8; /* MCK divided by 8 (MCK up to 20 MHz) */
+ }
+
+ sam_putreg(priv, SAM_EMAC_NCFGR_OFFSET, regval);
+
+ /* Check the PHY Address */
+
+ priv->phyaddr = priv->attr->phyaddr;
+ ret = sam_phyfind(priv, &priv->phyaddr);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: sam_phyfind failed: %d\n", ret);
+ return ret;
+ }
+
+ if (priv->phyaddr != priv->attr->phyaddr)
+ {
+ sam_phyreset(priv);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: sam_ethgpioconfig
+ *
+ * Description:
+ * Configure PIOs for the EMAC0/1 RMII/MII interface.
+ *
+ * Signal Name Function MII RMII
+ * -------- --------------------------------- -------- -----------
+ * TXCK Transmit Clock or Reference Clock TXCK REFCK
+ * TXEN Transmit Enable TXEN TXEN
+ * TX[3..0] Transmit Data TXD[3:0] TXD[1:0]
+ * TXER Transmit Coding Error TXER Not Used
+ * RXCK Receive Clock RXCK Not Used
+ * RXDV Receive Data Valid RXDV CRSDV
+ * RX[3..0] Receive Data RXD[3:0] RXD[1:0]
+ * RXER Receive Error RXER RXER
+ * CRS Carrier Sense and Data Valid CRS Not Used
+ * COL Collision Detect COL Not Used
+ * MDC Management Data Clock MDC MDC
+ * MDIO Management Data Input/Output MDIO MDIO
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline void sam_ethgpioconfig(struct sam_emac_s *priv)
+{
+#if defined(CONFIG_SAMV7_EMAC0)
+ /* Configure EMAC0 PIO pins */
+
+ if (priv->attr->emac == EMAC0_INTF)
+ {
+ /* Configure PIO pins common to RMII and MII mode*/
+
+ sam_configgpio(GPIO_EMAC0_TXCK); /* Transmit Clock (or Reference Clock) */
+ sam_configgpio(GPIO_EMAC0_TXEN); /* Transmit Enable */
+ sam_configgpio(GPIO_EMAC0_TX0); /* Transmit data TXD0 */
+ sam_configgpio(GPIO_EMAC0_TX1); /* Transmit data TXD1 */
+ sam_configgpio(GPIO_EMAC0_RXDV); /* Receive Data Valid */
+ sam_configgpio(GPIO_EMAC0_RX0); /* Receive data RXD0 */
+ sam_configgpio(GPIO_EMAC0_RX1); /* Receive data RXD0 */
+ sam_configgpio(GPIO_EMAC0_RXER); /* Receive Error */
+ sam_configgpio(GPIO_EMAC0_MDC); /* Management Data Clock */
+ sam_configgpio(GPIO_EMAC0_MDIO); /* Management Data Input/Output */
+
+ /* Configure additional PIO pins to support EMAC in MII mode*/
+
+ if (!priv->attr->rmii)
+ {
+ sam_configgpio(GPIO_EMAC0_TX2); /* Transmit data TXD2 */
+ sam_configgpio(GPIO_EMAC0_TX3); /* Transmit data TXD3 */
+ sam_configgpio(GPIO_EMAC0_TXER); /* Transmit Coding Error */
+ sam_configgpio(GPIO_EMAC0_RXCK); /* Receive Clock */
+ sam_configgpio(GPIO_EMAC0_RX2); /* Receive data RXD2 */
+ sam_configgpio(GPIO_EMAC0_RX3); /* Receive data RXD3 */
+ sam_configgpio(GPIO_EMAC0_CRS); /* Carrier Sense and Data Valid */
+ sam_configgpio(GPIO_EMAC0_COL); /* Collision Detect */
+ }
+ }
+ else
+#endif
+
+#if defined(CONFIG_SAMV7_EMAC1)
+ /* Configure EMAC0 PIO pins */
+
+ if (priv->attr->emac == EMAC1_INTF)
+ {
+ /* Configure PIO pins common to RMII and MII mode*/
+
+ sam_configgpio(GPIO_EMAC1_TXCK); /* Transmit Clock (or Reference Clock) */
+ sam_configgpio(GPIO_EMAC1_TXEN); /* Transmit Enable */
+ sam_configgpio(GPIO_EMAC1_TX0); /* Transmit data TXD0 */
+ sam_configgpio(GPIO_EMAC1_TX1); /* Transmit data TXD1 */
+ sam_configgpio(GPIO_EMAC1_RXDV); /* Receive Data Valid */
+ sam_configgpio(GPIO_EMAC1_RX0); /* Receive data RXD0 */
+ sam_configgpio(GPIO_EMAC1_RX1); /* Receive data RXD0 */
+ sam_configgpio(GPIO_EMAC1_RXER); /* Receive Error */
+ sam_configgpio(GPIO_EMAC1_MDC); /* Management Data Clock */
+ sam_configgpio(GPIO_EMAC1_MDIO); /* Management Data Input/Output */
+
+ /* Configure additional PIO pins to support EMAC in MII mode*/
+
+ if (!priv->attr->rmii)
+ {
+ sam_configgpio(GPIO_EMAC1_TX2); /* Transmit data TXD2 */
+ sam_configgpio(GPIO_EMAC1_TX3); /* Transmit data TXD3 */
+ sam_configgpio(GPIO_EMAC1_TXER); /* Transmit Coding Error */
+ sam_configgpio(GPIO_EMAC1_RXCK); /* Receive Clock */
+ sam_configgpio(GPIO_EMAC1_RX2); /* Receive data RXD2 */
+ sam_configgpio(GPIO_EMAC1_RX3); /* Receive data RXD3 */
+ sam_configgpio(GPIO_EMAC1_CRS); /* Carrier Sense and Data Valid */
+ sam_configgpio(GPIO_EMAC1_COL); /* Collision Detect */
+ }
+ }
+ else
+#endif
+ {
+ nvdbg("ERROR: emac=%d\n", priv->attr->emac);
+ }
+}
+
+/****************************************************************************
+ * Function: sam_txreset
+ *
+ * Description:
+ * Reset the transmit logic
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void sam_txreset(struct sam_emac_s *priv)
+{
+ uint8_t *txbuffer = priv->txbuffer;
+ struct emac_txdesc_s *txdesc = priv->txdesc;
+ uintptr_t bufaddr;
+ uint32_t regval;
+ int ndx;
+
+ /* Disable TX */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval &= ~EMAC_NCR_TXEN;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+
+ /* Configure the TX descriptors. */
+
+ priv->txhead = 0;
+ priv->txtail = 0;
+
+ for (ndx = 0; ndx < priv->attr->ntxbuffers; ndx++)
+ {
+ bufaddr = (uint32_t)(&(txbuffer[ndx * EMAC_TX_UNITSIZE]));
+
+ /* Set the buffer address and mark the descriptor as in used by firmware */
+
+ txdesc[ndx].addr = bufaddr;
+ txdesc[ndx].status = EMACTXD_STA_USED;
+ }
+
+ /* Mark the final descriptor in the list */
+
+ txdesc[priv->attr->ntxbuffers - 1].status =
+ EMACTXD_STA_USED | EMACTXD_STA_WRAP;
+
+#warning FIXME!!!
+#ifdef HAVE_CLEAN_DCACHE_RANGE
+ /* Flush the entire TX descriptor table to RAM */
+
+ arch_clean_dcache((uintptr_t)txdesc,
+ (uintptr_t)txdesc +
+ priv->attr->ntxbuffers * sizeof(struct emac_txdesc_s));
+#endif
+
+ /* Set the Transmit Buffer Queue Pointer Register */
+
+ sam_putreg(priv, SAM_EMAC_TBQB_OFFSET, (uint32_t)txdesc);
+}
+
+/****************************************************************************
+ * Function: sam_rxreset
+ *
+ * Description:
+ * Reset the receive logic
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void sam_rxreset(struct sam_emac_s *priv)
+{
+ struct emac_rxdesc_s *rxdesc = priv->rxdesc;
+ uint8_t *rxbuffer = priv->rxbuffer;
+ uint32_t bufaddr;
+ uint32_t regval;
+ int ndx;
+
+ /* Disable RX */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval &= ~EMAC_NCR_RXEN;
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+
+ /* Configure the RX descriptors. */
+
+ priv->rxndx = 0;
+ for (ndx = 0; ndx < priv->attr->nrxbuffers; ndx++)
+ {
+ bufaddr = (uintptr_t)(&(rxbuffer[ndx * EMAC_RX_UNITSIZE]));
+ DEBUGASSERT((bufaddr & ~EMACRXD_ADDR_MASK) == 0);
+
+ /* Set the buffer address and remove EMACRXD_ADDR_OWNER and
+ * EMACRXD_ADDR_WRAP.
+ */
+
+ rxdesc[ndx].addr = bufaddr;
+ rxdesc[ndx].status = 0;
+ }
+
+ /* Mark the final descriptor in the list */
+
+ rxdesc[priv->attr->nrxbuffers - 1].addr |= EMACRXD_ADDR_WRAP;
+
+#warning FIXME!!!
+#ifdef HAVE_CLEAN_DCACHE_RANGE
+ /* Flush the entire RX descriptor table to RAM */
+
+ arch_clean_dcache((uintptr_t)rxdesc,
+ (uintptr_t)rxdesc +
+ priv->attr->nrxbuffers * sizeof(struct emac_rxdesc_s));
+#endif
+
+ /* Set the Receive Buffer Queue Pointer Register */
+
+ sam_putreg(priv, SAM_EMAC_RBQB_OFFSET, (uint32_t)rxdesc);
+}
+
+/****************************************************************************
+ * Function: sam_emac_enableclk
+ *
+ * Description:
+ * Enable clocking to the EMAC block
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void sam_emac_enableclk(struct sam_emac_s *priv)
+{
+#if defined(CONFIG_SAMV7_EMAC0) && defined(CONFIG_SAMV7_EMAC1)
+ /* Both EMAC blocks are selected, which are we enabling? */
+
+ if (priv->attr->emac == EMAC0_INTF)
+ {
+ sam_emac0_enableclk();
+ }
+ else
+ {
+ sam_emac1_enableclk();
+ }
+
+#elif defined(CONFIG_SAMV7_EMAC0)
+ /* Only EMAC0 is selected */
+
+ sam_emac0_enableclk();
+
+#else
+ /* Only EMAC1 is selected */
+
+ sam_emac1_enableclk();
+#endif
+}
+
+/****************************************************************************
+ * Function: sam_emac_disableclk
+ *
+ * Description:
+ * Disable clocking to the EMAC block
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_NETDEV_PHY_IOCTL
+static void sam_emac_disableclk(struct sam_emac_s *priv)
+{
+#if defined(CONFIG_SAMV7_EMAC0) && defined(CONFIG_SAMV7_EMAC1)
+ /* Both EMAC blocks are selected, which are we disabling? */
+
+ if (priv->attr->emac == EMAC0_INTF)
+ {
+ sam_emac0_disableclk();
+ }
+ else
+ {
+ sam_emac1_disableclk();
+ }
+
+#elif defined(CONFIG_SAMV7_EMAC0)
+ /* Only EMAC0 is selected */
+
+ sam_emac0_disableclk();
+
+#else
+ /* Only EMAC1 is selected */
+
+ sam_emac1_disableclk();
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Function: sam_emac_reset
+ *
+ * Description:
+ * Reset the EMAC block.
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void sam_emac_reset(struct sam_emac_s *priv)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+ uint32_t regval;
+
+ /* We are supporting PHY IOCTLs, then do not reset the MAC. If we do,
+ * then we cannot communicate with the PHY. So, instead, just disable
+ * interrupts, cancel timers, and disable TX and RX.
+ */
+
+ sam_putreg(priv, SAM_EMAC_IDR_OFFSET, EMAC_INT_ALL);
+
+ /* Reset RX and TX logic */
+
+ sam_rxreset(priv);
+ sam_txreset(priv);
+
+ /* Disable Rx and Tx, plus the statistics registers. */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval &= ~(EMAC_NCR_RXEN | EMAC_NCR_TXEN | EMAC_NCR_WESTAT);
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+
+#else
+ /* Disable all EMAC interrupts */
+
+ sam_putreg(priv, SAM_EMAC_IDR_OFFSET, EMAC_INT_ALL);
+
+ /* Reset RX and TX logic */
+
+ sam_rxreset(priv);
+ sam_txreset(priv);
+
+ /* Make sure that RX and TX are disabled; clear statistics registers */
+
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, EMAC_NCR_CLRSTAT);
+
+ /* Disable clocking to the EMAC peripheral */
+
+ sam_emac_disableclk(priv);
+
+#endif
+}
+
+/****************************************************************************
+ * Function: sam_macaddress
+ *
+ * Description:
+ * Configure the selected MAC address.
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void sam_macaddress(struct sam_emac_s *priv)
+{
+ struct net_driver_s *dev = &priv->dev;
+ uint32_t regval;
+
+ nllvdbg("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->d_ifname,
+ dev->d_mac.ether_addr_octet[0], dev->d_mac.ether_addr_octet[1],
+ dev->d_mac.ether_addr_octet[2], dev->d_mac.ether_addr_octet[3],
+ dev->d_mac.ether_addr_octet[4], dev->d_mac.ether_addr_octet[5]);
+
+ /* Set the MAC address */
+
+ regval = (uint32_t)dev->d_mac.ether_addr_octet[0] |
+ (uint32_t)dev->d_mac.ether_addr_octet[1] << 8 |
+ (uint32_t)dev->d_mac.ether_addr_octet[2] << 16 |
+ (uint32_t)dev->d_mac.ether_addr_octet[3] << 24;
+ sam_putreg(priv, SAM_EMAC_SAB1_OFFSET, regval);
+
+ regval = (uint32_t)dev->d_mac.ether_addr_octet[4] |
+ (uint32_t)dev->d_mac.ether_addr_octet[5] << 8;
+ sam_putreg(priv, SAM_EMAC_SAT1_OFFSET, regval);
+}
+
+/****************************************************************************
+ * Function: sam_ipv6multicast
+ *
+ * Description:
+ * Configure the IPv6 multicast MAC address.
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void sam_ipv6multicast(struct sam_emac_s *priv)
+{
+ struct net_driver_s *dev;
+ uint16_t tmp16;
+ uint8_t mac[6];
+
+ /* For ICMPv6, we need to add the IPv6 multicast address
+ *
+ * For IPv6 multicast addresses, the Ethernet MAC is derived by
+ * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+ * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+ * to the Ethernet MAC address 33:33:00:01:00:03.
+ *
+ * NOTES: This appears correct for the ICMPv6 Router Solicitation
+ * Message, but the ICMPv6 Neighbor Solicitation message seems to
+ * use 33:33:ff:01:00:03.
+ */
+
+ mac[0] = 0x33;
+ mac[1] = 0x33;
+
+ dev = &priv->dev;
+ tmp16 = dev->d_ipv6addr[6];
+ mac[2] = 0xff;
+ mac[3] = tmp16 >> 8;
+
+ tmp16 = dev->d_ipv6addr[7];
+ mac[4] = tmp16 & 0xff;
+ mac[5] = tmp16 >> 8;
+
+ nvdbg("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+ (void)sam_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+ /* Add the IPv6 all link-local nodes Ethernet address. This is the
+ * address that we expect to receive ICMPv6 Router Advertisement
+ * packets.
+ */
+
+ (void)sam_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+ /* Add the IPv6 all link-local routers Ethernet address. This is the
+ * address that we expect to receive ICMPv6 Router Solicitation
+ * packets.
+ */
+
+ (void)sam_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+/****************************************************************************
+ * Function: sam_emac_configure
+ *
+ * Description:
+ * Configure the EMAC interface for normal operation.
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int sam_emac_configure(struct sam_emac_s *priv)
+{
+ uint32_t regval;
+
+ nllvdbg("Entry\n");
+
+ /* Enable clocking to the EMAC peripheral */
+
+ sam_emac_enableclk(priv);
+
+ /* Disable TX, RX, clear statistics. Disable all interrupts. */
+
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, EMAC_NCR_CLRSTAT);
+ sam_putreg(priv, SAM_EMAC_IDR_OFFSET, EMAC_INT_ALL);
+
+ /* Clear all status bits in the receive status register. */
+
+ regval = (EMAC_RSR_RXOVR | EMAC_RSR_REC | EMAC_RSR_BNA);
+ sam_putreg(priv, SAM_EMAC_RSR_OFFSET, regval);
+
+ /* Clear all status bits in the transmit status register */
+
+ regval = (EMAC_TSR_UBR | EMAC_TSR_COL | EMAC_TSR_RLE | EMAC_TSR_TFC |
+ EMAC_TSR_TXCOMP);
+ sam_putreg(priv, SAM_EMAC_TSR_OFFSET, regval);
+
+ /* Clear any pending interrupts */
+
+ (void)sam_getreg(priv, SAM_EMAC_ISR_OFFSET);
+
+ /* Enable/disable the copy of data into the buffers, ignore broadcasts.
+ * Don't copy FCS.
+ */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCFGR_OFFSET);
+ regval |= (EMAC_NCFGR_RFCS | EMAC_NCFGR_PEN);
+
+#ifdef CONFIG_NET_PROMISCUOUS
+ regval |= EMAC_NCFGR_CAF;
+#else
+ regval &= ~EMAC_NCFGR_CAF;
+#endif
+
+#ifdef CONFIG_SAMV7_EMAC_NBC
+ regval |= EMAC_NCFGR_NBC;
+#else
+ regval &= ~EMAC_NCFGR_NBC;
+#endif
+
+ sam_putreg(priv, SAM_EMAC_NCFGR_OFFSET, regval);
+
+ /* Reset TX and RX */
+
+ sam_rxreset(priv);
+ sam_txreset(priv);
+
+ /* Enable Rx and Tx, plus the statistics registers. */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR_OFFSET);
+ regval |= (EMAC_NCR_RXEN | EMAC_NCR_TXEN | EMAC_NCR_WESTAT);
+ sam_putreg(priv, SAM_EMAC_NCR_OFFSET, regval);
+
+ /* Setup the interrupts for TX events, RX events, and error events */
+
+ regval = (EMAC_INT_RCOMP | EMAC_INT_RXUBR | EMAC_INT_TUR | EMAC_INT_RLEX |
+ EMAC_INT_TFC | EMAC_INT_TCOMP | EMAC_INT_ROVR | EMAC_INT_HRESP |
+ EMAC_INT_PFNZ | EMAC_INT_PTZ);
+ sam_putreg(priv, SAM_EMAC_IER_OFFSET, regval);
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: sam_emac_initialize
+ *
+ * Description:
+ * Initialize the EMAC driver.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ * Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+int sam_emac_initialize(int intf)
+{
+ struct sam_emac_s *priv;
+ const struct sam_emacattr_s *attr;
+#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT)
+ uint8_t phytype;
+#endif
+ int ret;
+
+#if defined(CONFIG_SAMV7_EMAC0)
+ if (intf == EMAC0_INTF)
+ {
+ priv = &g_emac0;
+ attr = &g_emac0_attr;
+
+#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT)
+ phytype = SAMV7_EMAC0_PHY_TYPE;
+#endif
+ }
+ else
+#endif
+#if defined(CONFIG_SAMV7_EMAC1)
+ if (intf == EMAC1_INTF)
+ {
+ priv = &g_emac1;
+ attr = &g_emac1_attr;
+
+#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT)
+ phytype = SAMV7_EMAC1_PHY_TYPE;
+#endif
+ }
+ else
+#endif
+ {
+ ndbg("ERROR: Interface %d not supported\n", intf);
+ return -EINVAL;
+ }
+
+ /* Initialize the driver structure */
+
+ memset(priv, 0, sizeof(struct sam_emac_s));
+ priv->attr = attr; /* Save the constant attributes */
+ priv->dev.d_ifup = sam_ifup; /* I/F up (new IP address) callback */
+ priv->dev.d_ifdown = sam_ifdown; /* I/F down callback */
+ priv->dev.d_txavail = sam_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_IGMP
+ priv->dev.d_addmac = sam_addmac; /* Add multicast MAC address */
+ priv->dev.d_rmmac = sam_rmmac; /* Remove multicast MAC address */
+#endif
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+ priv->dev.d_ioctl = sam_ioctl; /* Support PHY ioctl() calls */
+#ifdef CONFIG_ARCH_PHY_INTERRUPT
+ priv->phytype = phytype; /* Type of PHY on port */
+#endif
+#endif
+
+ priv->dev.d_private = priv; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmissions */
+
+ priv->txpoll = wd_create();
+ if (!priv->txpoll)
+ {
+ ndbg("ERROR: Failed to create periodic poll timer\n");
+ ret = -EAGAIN;
+ goto errout;
+ }
+
+ priv->txtimeout = wd_create(); /* Create TX timeout timer */
+ if (!priv->txtimeout)
+ {
+ ndbg("ERROR: Failed to create periodic poll timer\n");
+ ret = -EAGAIN;
+ goto errout_with_txpoll;
+ }
+
+ /* Configure PIO pins to support EMAC */
+
+ sam_ethgpioconfig(priv);
+
+ /* Allocate buffers */
+
+ ret = sam_buffer_initialize(priv);
+ if (ret < 0)
+ {
+ ndbg("ERROR: sam_buffer_initialize failed: %d\n", ret);
+ goto errout_with_txtimeout;
+ }
+
+ /* Attach the IRQ to the driver. It will not be enabled at the AIC until
+ * the interface is in the 'up' state.
+ */
+
+ ret = irq_attach(priv->attr->irq, priv->attr->handler);
+ if (ret < 0)
+ {
+ ndbg("ERROR: Failed to attach the handler to the IRQ%d\n", priv->attr->irq);
+ goto errout_with_buffers;
+ }
+
+ /* Enable clocking to the EMAC peripheral (just for sam_ifdown()) */
+
+ sam_emac_enableclk(priv);
+
+ /* Put the interface in the down state (disabling clocking again). */
+
+ ret = sam_ifdown(&priv->dev);
+ if (ret < 0)
+ {
+ ndbg("ERROR: Failed to put the interface in the down state: %d\n", ret);
+ goto errout_with_buffers;
+ }
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ ret = netdev_register(&priv->dev, NET_LL_ETHERNET);
+ if (ret >= 0)
+ {
+ return ret;
+ }
+
+ ndbg("ERROR: netdev_register() failed: %d\n", ret);
+
+errout_with_buffers:
+ sam_buffer_free(priv);
+errout_with_txtimeout:
+ wd_delete(priv->txtimeout);
+errout_with_txpoll:
+ wd_delete(priv->txpoll);
+errout:
+ return ret;
+}
+
+#endif /* CONFIG_NET && CONFIG_SAMV7_EMAC */
diff --git a/nuttx/arch/arm/src/samv7/sam_ethernet.c b/nuttx/arch/arm/src/samv7/sam_ethernet.c
new file mode 100644
index 000000000..3e19d9587
--- /dev/null
+++ b/nuttx/arch/arm/src/samv7/sam_ethernet.c
@@ -0,0 +1,118 @@
+/****************************************************************************
+ * arch/arm/src/samv7/sam_ethernet.c
+ *
+ * Copyright (C) 2015 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <debug.h>
+#include "sam_ethernet.h"
+
+#ifdef CONFIG_NET
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: up_netinitialize
+ *
+ * Description:
+ * This is the "standard" network initialization logic called from the
+ * low-level initialization logic in up_initialize.c. This is just
+ * a shim to support the slightly different prototype of
+ * sam_emac_intiialize() and to provide support for future chips that
+ * may have multiple EMAC peripherals.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+void up_netinitialize(void)
+{
+#ifdef CONFIG_SAMV7_EMAC0
+ int ret;
+
+#ifdef CONFIG_SAMV7_EMAC0
+ /* Initialize the EMAC0 driver */
+
+ ret = sam_emac_initialize(EMAC0_INTF);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: up_emac_initialize(EMAC0) failed: %d\n", ret);
+ }
+#endif
+
+#ifdef CONFIG_SAMV7_EMAC1
+ /* Initialize the EMAC1 driver */
+
+ ret = sam_emac_initialize(EMAC1_INTF);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: up_emac_initialize(EMAC1) failed: %d\n", ret);
+ }
+#endif
+#endif
+}
+
+#endif /* CONFIG_NET */
diff --git a/nuttx/arch/arm/src/samv7/sam_ethernet.h b/nuttx/arch/arm/src/samv7/sam_ethernet.h
new file mode 100644
index 000000000..0772e0a8c
--- /dev/null
+++ b/nuttx/arch/arm/src/samv7/sam_ethernet.h
@@ -0,0 +1,234 @@
+/************************************************************************************
+ * arch/arm/src/samv7/sam_ethernet.h
+ *
+ * Copyright (C) 2015 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ************************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_SAMV7_SAM_ETHERNET_H
+#define __ARCH_ARM_SRC_SAMV7_SAM_ETHERNET_H
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+#include <arch/samv7/chip.h>
+
+#include "chip/sam_emac.h"
+
+/************************************************************************************
+ * Pre-processor Definitions
+ ************************************************************************************/
+/* Understood PHY types */
+
+#define SAMV7_PHY_DM9161 0
+#define SAMV7_PHY_LAN8700 1
+#define SAMV7_PHY_KSZ8051 2
+#define SAMV7_PHY_KSZ8061 3
+#define SAMV7_PHY_KSZ8081 4
+#define SAMV7_PHY_KSZ90x1 5
+
+/* Definitions for use with sam_phy_boardinitialize */
+
+#define EMAC0_INTF 0
+#define EMAC1_INTF 1
+
+/* Which is ETH0 and which is ETH1? */
+
+#ifndef CONFIG_SAMV7_EMAC
+# undef CONFIG_SAMV7_EMAC0_ISETH0
+# undef CONFIG_SAMV7_EMAC1_ISETH0
+#endif
+
+#if defined(CONFIG_SAMV7_EMAC0_ISETH0) && defined(CONFIG_SAMV7_EMAC1_ISETH0)
+# error EMAC0 and EMAC2 cannot both be ETH0
+#endif
+
+#if defined(CONFIG_SAMV7_EMAC0_ISETH0)
+# if defined(CONFIG_ETH0_PHY_DM9161)
+# define SAMV7_EMAC0_PHY_DM9161 1
+# define SAMV7_EMAC0_PHY_TYPE SAMV7_PHY_DM9161
+# elif defined(CONFIG_ETH0_PHY_LAN8700)
+# define SAMV7_EMAC0_PHY_LAN8700 1
+# define SAMV7_EMAC0_PHY_TYPE SAMV7_PHY_LAN8700
+# elif defined(CONFIG_ETH0_PHY_KSZ8051)
+# define SAMV7_EMAC0_PHY_KSZ8051 1
+# define SAMV7_EMAC0_PHY_TYPE SAMV7_PHY_KSZ8051
+# elif defined(CONFIG_ETH0_PHY_KSZ8061)
+# define SAMV7_EMAC0_PHY_KSZ8061 1
+# define SAMV7_EMAC0_PHY_TYPE SAMV7_PHY_KSZ8061
+# elif defined(CONFIG_ETH0_PHY_KSZ8081)
+# define SAMV7_EMAC0_PHY_KSZ8081 1
+# define SAMV7_EMAC0_PHY_TYPE SAMV7_PHY_KSZ8081
+# elif defined(CONFIG_ETH0_PHY_KSZ90x1)
+# define SAMV7_EMAC0_PHY_KSZ90x1 1
+# define SAMV7_EMAC0_PHY_TYPE SAMV7_PHY_KSZ90x1
+# else
+# error ETH0 PHY unrecognized
+# endif
+#elif defined(CONFIG_SAMV7_EMAC0)
+# if defined(CONFIG_ETH1_PHY_DM9161)
+# define SAMV7_EMAC0_PHY_DM9161 1
+# define SAMV7_EMAC0_PHY_TYPE SAMV7_PHY_DM9161
+# elif defined(CONFIG_ETH1_PHY_LAN8700)
+# define SAMV7_EMAC0_PHY_LAN8700 1
+# define SAMV7_EMAC0_PHY_TYPE SAMV7_PHY_LAN8700
+# elif defined(CONFIG_ETH1_PHY_KSZ8051)
+# define SAMV7_EMAC0_PHY_KSZ8051 1
+# define SAMV7_EMAC0_PHY_TYPE SAMV7_PHY_KSZ8051
+# elif defined(CONFIG_ETH1_PHY_KSZ8061)
+# define SAMV7_EMAC0_PHY_KSZ8061 1
+# define SAMV7_EMAC0_PHY_TYPE SAMV7_PHY_KSZ8061
+# elif defined(CONFIG_ETH0_PHY_KSZ8081)
+# define SAMV7_EMAC0_PHY_KSZ8081 1
+# define SAMV7_EMAC0_PHY_TYPE SAMV7_PHY_KSZ8081
+# elif defined(CONFIG_ETH1_PHY_KSZ90x1)
+# define SAMV7_EMAC0_PHY_KSZ90x1 1
+# define SAMV7_EMAC0_PHY_TYPE SAMV7_PHY_KSZ90x1
+# else
+# error ETH1 PHY unrecognized
+# endif
+#endif
+
+#if defined(CONFIG_SAMV7_EMAC1_ISETH0)
+# if defined(CONFIG_ETH0_PHY_DM9161)
+# define SAMV7_EMAC1_PHY_DM9161 1
+# define SAMV7_EMAC1_PHY_TYPE SAMV7_PHY_DM9161
+# elif defined(CONFIG_ETH0_PHY_LAN8700)
+# define SAMV7_EMAC1_PHY_LAN8700 1
+# define SAMV7_EMAC1_PHY_TYPE SAMV7_PHY_LAN8700
+# elif defined(CONFIG_ETH0_PHY_KSZ8051)
+# define SAMV7_EMAC1_PHY_KSZ8051 1
+# define SAMV7_EMAC1_PHY_TYPE SAMV7_PHY_KSZ8051
+# elif defined(CONFIG_ETH0_PHY_KSZ8061)
+# define SAMV7_EMAC1_PHY_KSZ8061 1
+# define SAMV7_EMAC1_PHY_TYPE SAMV7_PHY_KSZ8061
+# elif defined(CONFIG_ETH0_PHY_KSZ8081)
+# define SAMV7_EMAC1_PHY_KSZ8081 1
+# define SAMV7_EMAC1_PHY_TYPE SAMV7_PHY_KSZ8081
+# elif defined(CONFIG_ETH0_PHY_KSZ90x1)
+# define SAMV7_EMAC1_PHY_KSZ90x1 1
+# define SAMV7_EMAC1_PHY_TYPE SAMV7_PHY_KSZ90x1
+# else
+# error ETH0 PHY unrecognized
+# endif
+#elif defined(CONFIG_SAMV7_EMAC1)
+# if defined(CONFIG_ETH1_PHY_DM9161)
+# define SAMV7_EMAC1_PHY_DM9161 1
+# define SAMV7_EMAC1_PHY_TYPE SAMV7_PHY_DM9161
+# elif defined(CONFIG_ETH1_PHY_LAN8700)
+# define SAMV7_EMAC1_PHY_LAN8700 1
+# define SAMV7_EMAC1_PHY_TYPE SAMV7_PHY_LAN8700
+# elif defined(CONFIG_ETH1_PHY_KSZ8051)
+# define SAMV7_EMAC1_PHY_KSZ8051 1
+# define SAMV7_EMAC1_PHY_TYPE SAMV7_PHY_KSZ8051
+# elif defined(CONFIG_ETH1_PHY_KSZ8061)
+# define SAMV7_EMAC1_PHY_KSZ8061 1
+# define SAMV7_EMAC1_PHY_TYPE SAMV7_PHY_KSZ8061
+# elif defined(CONFIG_ETH0_PHY_KSZ8081)
+# define SAMV7_EMAC1_PHY_KSZ8081 1
+# define SAMV7_EMAC1_PHY_TYPE SAMV7_PHY_KSZ8081
+# elif defined(CONFIG_ETH1_PHY_KSZ90x1)
+# define SAMV7_EMAC1_PHY_KSZ90x1 1
+# define SAMV7_EMAC1_PHY_TYPE SAMV7_PHY_KSZ90x1
+# else
+# error ETH1 PHY unrecognized
+# endif
+#endif
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Function: sam_emac_initialize
+ *
+ * Description:
+ * Initialize the EMAC driver.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ * Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMV7_EMAC
+int sam_emac_initialize(int intf);
+#endif
+
+/************************************************************************************
+ * Function: sam_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_SAMV7_PHYINIT is defined in the configuration then the board specific
+ * logic must provide sam_phyinitialize(); The SAMV7 Ethernet driver will call
+ * this function one time before it first uses the PHY.
+ *
+ * Parameters:
+ * intf - Always zero for now.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ************************************************************************************/
+
+#ifdef CONFIG_SAMV7_PHYINIT
+int sam_phy_boardinitialize(int intf);
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_ARM_SRC_SAMV7_SAM_ETHERNET_H */
diff --git a/nuttx/arch/arm/src/samv7/samv71_periphclks.h b/nuttx/arch/arm/src/samv7/samv71_periphclks.h
index 35799bf5a..6a360dba4 100644
--- a/nuttx/arch/arm/src/samv7/samv71_periphclks.h
+++ b/nuttx/arch/arm/src/samv7/samv71_periphclks.h
@@ -96,7 +96,7 @@
#define sam_mcan01_enableclk()
#define sam_mcan10_enableclk() sam_enableperiph1(SAM_PID_MCAN10)
#define sam_mcan11_enableclk()
-#define sam_emac_enableclk() sam_enableperiph1(SAM_PID_EMAC)
+#define sam_emac0_enableclk() sam_enableperiph1(SAM_PID_EMAC0)
#define sam_afec1_enableclk() sam_enableperiph1(SAM_PID_AFEC1)
#define sam_twihs2_enableclk() sam_enableperiph1(SAM_PID_TWIHS2)
#define sam_spi1_enableclk() sam_enableperiph1(SAM_PID_SPI1)
@@ -164,7 +164,7 @@
#define sam_mcan01_disableclk()
#define sam_mcan10_disableclk() sam_disableperiph1(SAM_PID_MCAN10)
#define sam_mcan11_disableclk()
-#define sam_emac_disableclk() sam_disableperiph1(SAM_PID_EMAC)
+#define sam_emac0_disableclk() sam_disableperiph1(SAM_PID_EMAC0)
#define sam_afec1_disableclk() sam_disableperiph1(SAM_PID_AFEC1)
#define sam_twihs2_disableclk() sam_disableperiph1(SAM_PID_TWIHS2)
#define sam_spi1_disableclk() sam_disableperiph1(SAM_PID_SPI1)
diff --git a/nuttx/configs/samv71-xult/README.txt b/nuttx/configs/samv71-xult/README.txt
index e012f2d41..1afa25013 100644
--- a/nuttx/configs/samv71-xult/README.txt
+++ b/nuttx/configs/samv71-xult/README.txt
@@ -15,6 +15,7 @@ Contents
- Automounter
- LEDs and Buttons
- AT24MAC402 Serial EEPROM
+ - Networking
- Debugging
- Configurations
@@ -87,6 +88,11 @@ The BASIC nsh configuration is fully function (as desribed below under
drivers will port easily from either the SAM3/4 or from the SAMA5Dx.
So there is still plenty to be done.
+ 6. There has been a quick'n'dirty port of the SAMA5D4-EK Ethernet logic
+ for the SAMV71-XULT. There are still some cache-related issues to
+ be verified. No testing has yet been performed and so the driver should
+ be considered non-functional.
+
Serial Console
==============
@@ -360,6 +366,243 @@ I2C address:
of the A0, A1, and A3 pins on the part. On the SAMV71-XULT board, these
are all pulled high so the full, 7-bit address is 0x5f.
+Networking
+==========
+
+KSZ8061RNBVA Connections
+------------------------
+
+ ------ --------- --------- --------------------------
+ SAMV71 SAMV71 Ethernet Shared functionality
+ Pin Function Function
+ ------ --------- --------- --------------------------
+ PD00 GTXCK REF_CLK Shield
+ PD01 GTXEN TXEN
+ PD02 GTX0 TXD0
+ PD03 GTX1 TXD1
+ PD04 GRXDV CRS_DV Trace
+ PD05 GRX0 RXD0 Trace
+ PD06 GRX1 RXD1 Trace
+ PD07 GRXER RXER Trace
+ PD08 GMDC MDC Trace
+ PD09 GMDIO MDIO
+ PA19 GPIO INTERRUPT EXT1, Shield
+ PA29 GPIO SIGDET
+ PC10 GPIO RESET
+ ------ --------- --------- --------------------------
+
+Selecting the GMAC peripheral
+-----------------------------
+
+ System Type -> SAMV7 Peripheral Support
+ CONFIG_SAMV7_EMAC0=y : Enable the GMAC peripheral (aka, EMAC0)
+
+ System Type -> EMAC device driver options
+ CONFIG_SAMV7_EMAC0_NRXBUFFERS=16 : Set aside some RS and TX buffers
+ CONFIG_SAMV7_EMAC0_NTXBUFFERS=8
+ CONFIG_SAMV7_EMAC0_RMII=y : The RMII interfaces is used on the board
+ CONFIG_SAMV7_EMAC0_AUTONEG=y : Use autonegotiation
+ CONFIG_SAMV7_EMAC0_PHYADDR=1 : KSZ8061 PHY is at address 1
+ CONFIG_SAMV7_EMAC0_PHYSR=30 : Address of PHY status register on KSZ8061
+ CONFIG_SAMV7_EMAC0_PHYSR_ALTCONFIG=y : Needed for KSZ8061
+ CONFIG_SAMV7_EMAC0_PHYSR_ALTMODE=0x7 : " " " " " "
+ CONFIG_SAMV7_EMAC0_PHYSR_10HD=0x1 : " " " " " "
+ CONFIG_SAMV7_EMAC0_PHYSR_100HD=0x2 : " " " " " "
+ CONFIG_SAMV7_EMAC0_PHYSR_10FD=0x5 : " " " " " "
+ CONFIG_SAMV7_EMAC0_PHYSR_100FD=0x6 : " " " " " "
+
+ PHY selection. Later in the configuration steps, you will need to select
+ the KSZ8061 PHY for EMAC (See below)
+
+ Networking Support
+ CONFIG_NET=y : Enable Neworking
+ CONFIG_NET_SOCKOPTS=y : Enable socket operations
+ CONFIG_NET_ETH_MTU=562 : Maximum packet size (MTU) 1518 is more standard
+ CONFIG_NET_ETH_TCP_RECVWNDO=562 : Should be the same as CONFIG_NET_ETH_MTU
+ CONFIG_NET_ARP=y : ARP support should be enabled
+ CONFIG_NET_ARP_SEND=y : Use ARP to get peer address before sending
+ CONFIG_NET_TCP=y : Enable TCP/IP networking
+ CONFIG_NET_TCPBACKLOG=y : Support TCP/IP backlog
+ CONFIG_NET_TCP_READAHEAD=y : Enable TCP read-ahead buffering
+ CONFIG_NET_TCP_WRITE_BUFFERS=y : Enable TCP write buffering
+ CONFIG_NET_UDP=y : Enable UDP networking
+ CONFIG_NET_BROADCAST=y : Support UDP broadcase packets
+ CONFIG_NET_ICMP=y : Enable ICMP networking
+ CONFIG_NET_ICMP_PING=y : Needed for NSH ping command
+ : Defaults should be okay for other options
+ Device drivers -> Network Device/PHY Support
+ CONFIG_NETDEVICES=y : Enabled PHY selection
+ CONFIG_ETH0_PHY_KSZ8061=y : Select the KSZ8061 PHY used with EMAC0
+
+ Application Configuration -> Network Utilities
+ CONFIG_NETUTILS_DNSCLIENT=y : Enable host address resolution
+ CONFIG_NETUTILS_TELNETD=y : Enable the Telnet daemon
+ CONFIG_NETUTILS_TFTPC=y : Enable TFTP data file transfers for get and put commands
+ CONFIG_NETUTILS_NETLIB=y : Network library support is needed
+ CONFIG_NETUTILS_WEBCLIENT=y : Needed for wget support
+ : Defaults should be okay for other options
+ Application Configuration -> NSH Library
+ CONFIG_NSH_TELNET=y : Enable NSH session via Telnet
+ CONFIG_NSH_IPADDR=0x0a000002 : Select an IP address
+ CONFIG_NSH_DRIPADDR=0x0a000001 : IP address of gateway/host PC
+ CONFIG_NSH_NETMASK=0xffffff00 : Netmask
+ CONFIG_NSH_NOMAC=y : Need to make up a bogus MAC address
+ : Defaults should be okay for other options
+
+Using the network with NSH
+--------------------------
+
+So what can you do with this networking support? First you see that
+NSH has several new network related commands:
+
+ ifconfig, ifdown, ifup: Commands to help manage your network
+ get and put: TFTP file transfers
+ wget: HTML file transfers
+ ping: Check for access to peers on the network
+ Telnet console: You can access the NSH remotely via telnet.
+
+You can also enable other add on features like full FTP or a Web
+Server or XML RPC and others. There are also other features that
+you can enable like DHCP client (or server) or network name
+resolution.
+
+By default, the IP address of the SAMA4D4-EK will be 10.0.0.2 and
+it will assume that your host is the gateway and has the IP address
+10.0.0.1.
+
+ nsh> ifconfig
+ eth0 HWaddr 00:e0:de:ad:be:ef at UP
+ IPaddr:10.0.0.2 DRaddr:10.0.0.1 Mask:255.255.255.0
+
+You can use ping to test for connectivity to the host (Careful,
+Window firewalls usually block ping-related ICMP traffic). On the
+target side, you can:
+
+ nsh> ping 10.0.0.1
+ PING 10.0.0.1 56 bytes of data
+ 56 bytes from 10.0.0.1: icmp_seq=1 time=0 ms
+ 56 bytes from 10.0.0.1: icmp_seq=2 time=0 ms
+ 56 bytes from 10.0.0.1: icmp_seq=3 time=0 ms
+ 56 bytes from 10.0.0.1: icmp_seq=4 time=0 ms
+ 56 bytes from 10.0.0.1: icmp_seq=5 time=0 ms
+ 56 bytes from 10.0.0.1: icmp_seq=6 time=0 ms
+ 56 bytes from 10.0.0.1: icmp_seq=7 time=0 ms
+ 56 bytes from 10.0.0.1: icmp_seq=8 time=0 ms
+ 56 bytes from 10.0.0.1: icmp_seq=9 time=0 ms
+ 56 bytes from 10.0.0.1: icmp_seq=10 time=0 ms
+ 10 packets transmitted, 10 received, 0% packet loss, time 10100 ms
+
+NOTE: In this configuration is is normal to have packet loss > 0%
+the first time you ping due to the default handling of the ARP
+table.
+
+On the host side, you should also be able to ping the SAMA4D4-EK:
+
+ $ ping 10.0.0.2
+
+You can also log into the NSH from the host PC like this:
+
+ $ telnet 10.0.0.2
+ Trying 10.0.0.2...
+ Connected to 10.0.0.2.
+ Escape character is '^]'.
+ sh_telnetmain: Session [3] Started
+
+ NuttShell (NSH) NuttX-7.9
+ nsh> help
+ help usage: help [-v] [<cmd>]
+
+ [ echo ifconfig mkdir mw sleep
+ ? exec ifdown mkfatfs ping test
+ cat exit ifup mkfifo ps umount
+ cp free kill mkrd put usleep
+ cmp get losetup mh rm wget
+ dd help ls mount rmdir xd
+ df hexdump mb mv sh
+
+ Builtin Apps:
+ nsh>
+
+NOTE: If you enable this feature, you experience a delay on booting.
+That is because the start-up logic waits for the network connection
+to be established before starting NuttX. In a real application, you
+would probably want to do the network bringup on a separate thread
+so that access to the NSH prompt is not delayed.
+
+This delay will be especially long if the board is not connected to
+a network. On the order of a minute! You will probably think that
+NuttX has crashed! And then, when it finally does come up, the
+network will not be available.
+
+Network Initialization Thread
+-----------------------------
+There is a configuration option enabled by CONFIG_NSH_NETINIT_THREAD
+that will do the NSH network bring-up asynchronously in parallel on
+a separate thread. This eliminates the (visible) networking delay
+altogether. This networking initialization feature by itself has
+some limitations:
+
+ - If no network is connected, the network bring-up will fail and
+ the network initialization thread will simply exit. There are no
+ retries and no mechanism to know if the network initialization was
+ successful.
+
+ - Furthermore, there is no support for detecting loss of the network
+ connection and recovery of networking when the connection is restored.
+
+Both of these shortcomings can be eliminated by enabling the network
+monitor:
+
+Network Monitor
+---------------
+By default the network initialization thread will bring-up the network
+then exit, freeing all of the resources that it required. This is a
+good behavior for systems with limited memory.
+
+If the CONFIG_NSH_NETINIT_MONITOR option is selected, however, then the
+network initialization thread will persist forever; it will monitor the
+network status. In the event that the network goes down (for example, if
+a cable is removed), then the thread will monitor the link status and
+attempt to bring the network back up. In this case the resources
+required for network initialization are never released.
+
+Pre-requisites:
+
+ - CONFIG_NSH_NETINIT_THREAD as described above.
+
+ - CONFIG_NETDEV_PHY_IOCTL. Enable PHY IOCTL commands in the Ethernet
+ device driver. Special IOCTL commands must be provided by the Ethernet
+ driver to support certain PHY operations that will be needed for link
+ management. There operations are not complex and are implemented for
+ the Atmel SAMV7 family.
+
+ - CONFIG_ARCH_PHY_INTERRUPT. This is not a user selectable option.
+ Rather, it is set when you select a board that supports PHY interrupts.
+ In most architectures, the PHY interrupt is not associated with the
+ Ethernet driver at all. Rather, the PHY interrupt is provided via some
+ board-specific GPIO and the board-specific logic must provide support
+ for that GPIO interrupt. To do this, the board logic must do two things:
+ (1) It must provide the function arch_phy_irq() as described and
+ prototyped in the nuttx/include/nuttx/arch.h, and (2) it must select
+ CONFIG_ARCH_PHY_INTERRUPT in the board configuration file to advertise
+ that it supports arch_phy_irq(). This logic can be found at
+ nuttx/configs/sama5d4-ek/src/sam_ethernet.c.
+
+ - And a few other things: UDP support is required (CONFIG_NET_UDP) and
+ signals must not be disabled (CONFIG_DISABLE_SIGNALS).
+
+Given those prerequisites, the network monitor can be selected with these
+additional settings.
+
+ Networking Support -> Networking Device Support
+ CONFIG_NETDEV_PHY_IOCTL=y : Enable PHY ioctl support
+
+ Application Configuration -> NSH Library -> Networking Configuration
+ CONFIG_NSH_NETINIT_THREAD : Enable the network initialization thread
+ CONFIG_NSH_NETINIT_MONITOR=y : Enable the network monitor
+ CONFIG_NSH_NETINIT_RETRYMSEC=2000 : Configure the network monitor as you like
+ CONFIG_NSH_NETINIT_SIGNO=18
+
Debugging
=========
diff --git a/nuttx/configs/samv71-xult/src/sam_ethernet.c b/nuttx/configs/samv71-xult/src/sam_ethernet.c
new file mode 100644
index 000000000..966886ec9
--- /dev/null
+++ b/nuttx/configs/samv71-xult/src/sam_ethernet.c
@@ -0,0 +1,272 @@
+/************************************************************************************
+ * configs/samv71-xult/src/sam_ethernet.c
+ *
+ * Copyright (C) 2015 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ************************************************************************************/
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+/* Force verbose debug on in this file only to support unit-level testing. */
+
+#ifdef CONFIG_NETDEV_PHY_DEBUG
+# undef CONFIG_DEBUG_VERBOSE
+# define CONFIG_DEBUG_VERBOSE 1
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include "sam_gpio.h"
+#include "sam_ethernet.h"
+
+#include "samv71-xult.h"
+
+#ifdef HAVE_NETWORK
+
+/************************************************************************************
+ * Pre-processor Definitions
+ ************************************************************************************/
+
+#define SAMV7_EMAC0_DEVNAME "eth0"
+
+/* Debug ********************************************************************/
+/* Extra, in-depth debug output that is only available if
+ * CONFIG_NETDEV_PHY_DEBUG us defined.
+ */
+
+#ifdef CONFIG_NETDEV_PHY_DEBUG
+# define phydbg dbg
+# define phylldbg lldbg
+#else
+# define phydbg(x...)
+# define phylldbg(x...)
+#endif
+
+/************************************************************************************
+ * Private Data
+ ************************************************************************************/
+
+#ifdef CONFIG_SAMV7_PIOA_IRQ
+static xcpt_t g_emac0_handler;
+#endif
+
+/************************************************************************************
+ * Private Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: sam_emac_phy_enable and sam_gmac_enable
+ ************************************************************************************/
+
+#ifdef CONFIG_SAMV7_PIOA_IRQ
+static void sam_emac0_phy_enable(bool enable)
+{
+ phydbg("IRQ%d: enable=%d\n", IRQ_INT_ETH0, enable);
+ if (enable)
+ {
+ sam_pioirqenable(IRQ_INT_ETH0);
+ }
+ else
+ {
+ sam_pioirqdisable(IRQ_INT_ETH0);
+ }
+}
+#endif
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Name: sam_netinitialize
+ *
+ * Description:
+ * Configure board resources to support networking.
+ *
+ ************************************************************************************/
+
+void weak_function sam_netinitialize(void)
+{
+ phydbg("Configuring %08x\n", GPIO_INT_ETH0);
+ sam_configgpio(GPIO_INT_ETH0);
+}
+
+/****************************************************************************
+ * Name: arch_phy_irq
+ *
+ * Description:
+ * This function may be called to register an interrupt handler that will
+ * be called when a PHY interrupt occurs. This function both attaches
+ * the interrupt handler and enables the interrupt if 'handler' is non-
+ * NULL. If handler is NULL, then the interrupt is detached and disabled
+ * instead.
+ *
+ * The PHY interrupt is always disabled upon return. The caller must
+ * call back through the enable function point to control the state of
+ * the interrupt.
+ *
+ * This interrupt may or may not be available on a given platform depending
+ * on how the network hardware architecture is implemented. In a typical
+ * case, the PHY interrupt is provided to board-level logic as a GPIO
+ * interrupt (in which case this is a board-specific interface and really
+ * should be called board_phy_irq()); In other cases, the PHY interrupt
+ * may be cause by the chip's MAC logic (in which case arch_phy_irq()) is
+ * an appropriate name. Other other boards, there may be no PHY interrupts
+ * available at all. If client attachable PHY interrupts are available
+ * from the board or from the chip, then CONFIG_ARCH_PHY_INTERRUPT should
+ * be defined to indicate that fact.
+ *
+ * Typical usage:
+ * a. OS service logic (not application logic*) attaches to the PHY
+ * PHY interrupt and enables the PHY interrupt.
+ * b. When the PHY interrupt occurs: (1) the interrupt should be
+ * disabled and () work should be scheduled on the worker thread (or
+ * perhaps a dedicated application thread).
+ * c. That worker thread should use the SIOCGMIIPHY, SIOCGMIIREG,
+ * and SIOCSMIIREG ioctl calls** to communicate with the PHY,
+ * determine what network event took place (Link Up/Down?), and
+ * take the appropriate actions.
+ * d. It should then interact the the PHY to clear any pending
+ * interrupts, then re-enable the PHY interrupt.
+ *
+ * * This is an OS internal interface and should not be used from
+ * application space. Rather applications should use the SIOCMIISIG
+ * ioctl to receive a signal when a PHY event occurs.
+ * ** This interrupt is really of no use if the Ethernet MAC driver
+ * does not support these ioctl calls.
+ *
+ * Input Parameters:
+ * intf - Identifies the network interface. For example "eth0". Only
+ * useful on platforms that support multiple Ethernet interfaces
+ * and, hence, multiple PHYs and PHY interrupts.
+ * handler - The client interrupt handler to be invoked when the PHY
+ * asserts an interrupt. Must reside in OS space, but can
+ * signal tasks in user space. A value of NULL can be passed
+ * in order to detach and disable the PHY interrupt.
+ * enable - A function pointer that be unsed to enable or disable the
+ * PHY interrupt.
+ *
+ * Returned Value:
+ * The previous PHY interrupt handler address is returned. This allows you
+ * to temporarily replace an interrupt handler, then restore the original
+ * interrupt handler. NULL is returned if there is was not handler in
+ * place when the call was made.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMV7_PIOA_IRQ
+xcpt_t arch_phy_irq(FAR const char *intf, xcpt_t handler, phy_enable_t *enable)
+{
+ irqstate_t flags;
+ xcpt_t *phandler;
+ xcpt_t oldhandler;
+ pio_pinset_t pinset;
+ phy_enable_t enabler;
+ int irq;
+
+ DEBUGASSERT(intf);
+
+ nvdbg("%s: handler=%p\n", intf, handler);
+ phydbg("EMAC0: devname=%s\n", SAMV7_EMAC0_DEVNAME);
+ phydbg("EMAC1: devname=%s\n", SAMV7_EMAC1_DEVNAME);
+
+ if (strcmp(intf, SAMV7_EMAC0_DEVNAME) == 0)
+ {
+ phydbg("Select EMAC0\n");
+ phandler = &g_emac0_handler;
+ pinset = GPIO_INT_ETH0;
+ irq = IRQ_INT_ETH0;
+ enabler = sam_emac0_phy_enable;
+ }
+ else
+ {
+ ndbg("Unsupported interface: %s\n", intf);
+ return NULL;
+ }
+
+ /* Disable interrupts until we are done. This guarantees that the
+ * following operations are atomic.
+ */
+
+ flags = irqsave();
+
+ /* Get the old interrupt handler and save the new one */
+
+ oldhandler = *phandler;
+ *phandler = handler;
+
+ /* Configure the interrupt */
+
+ if (handler)
+ {
+ phydbg("Configure pin: %08x\n", pinset);
+ sam_pioirq(pinset);
+
+ phydbg("Attach IRQ%d\n", irq);
+ (void)irq_attach(irq, handler);
+ }
+ else
+ {
+ phydbg("Detach IRQ%d\n", irq);
+ (void)irq_detach(irq);
+ enabler = NULL;
+ }
+
+ /* Return with the interrupt disabled in either case */
+
+ sam_pioirqdisable(irq);
+
+ /* Return the enabling function pointer */
+
+ if (enable)
+ {
+ *enable = enabler;
+ }
+
+ /* Return the old handler (so that it can be restored) */
+
+ irqrestore(flags);
+ return oldhandler;
+}
+#endif /* CONFIG_SAMV7_PIOA_IRQ */
+
+#endif /* HAVE_NETWORK */
diff --git a/nuttx/configs/samv71-xult/src/samv71-xult.h b/nuttx/configs/samv71-xult/src/samv71-xult.h
index 934f7294d..db74840c6 100644
--- a/nuttx/configs/samv71-xult/src/samv71-xult.h
+++ b/nuttx/configs/samv71-xult/src/samv71-xult.h
@@ -169,9 +169,34 @@
*/
/* Ethernet MAC.
- * to be provided
+ *
+ * KSZ8061RNBVA Connections
+ * ------------------------
+ *
+ * ------ --------- --------- --------------------------
+ * SAMV71 SAMV71 Ethernet Shared functionality
+ * Pin Function Function
+ * ------ --------- --------- --------------------------
+ * PD00 GTXCK REF_CLK Shield
+ * PD01 GTXEN TXEN
+ * PD02 GTX0 TXD0
+ * PD03 GTX1 TXD1
+ * PD04 GRXDV CRS_DV Trace
+ * PD05 GRX0 RXD0 Trace
+ * PD06 GRX1 RXD1 Trace
+ * PD07 GRXER RXER Trace
+ * PD08 GMDC MDC Trace
+ * PD09 GMDIO MDIO
+ * PA19 GPIO INTERRUPT EXT1, Shield
+ * PA29 GPIO SIGDET
+ * PC10 GPIO RESET
+ * ------ --------- --------- --------------------------
*/
+#define GPIO_INT_ETH0 (GPIO_INPUT | GPIO_CFG_PULLUP | GPIO_CFG_DEGLITCH | \
+ GPIO_INT_FALLING | GPIO_PORT_PIOA | GPIO_PIN19)
+#define IRQ_INT_ETH0 SAM_IRQ_PA19
+
/* LEDs
*
* There are two yellow LED available on the SAM V71 Xplained Ultra board that
diff --git a/nuttx/drivers/net/Kconfig b/nuttx/drivers/net/Kconfig
index 4a1227892..134acc68f 100644
--- a/nuttx/drivers/net/Kconfig
+++ b/nuttx/drivers/net/Kconfig
@@ -296,6 +296,9 @@ config ETH0_PHY_KS8721
config ETH0_PHY_KSZ8051
bool "Micrel KSZ8051 PHY"
+config ETH0_PHY_KSZ8061
+ bool "Micrel KSZ8061 PHY"
+
config ETH0_PHY_KSZ8081
bool "Micrel KSZ8081 PHY"