summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-03-12 15:03:59 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-03-12 15:03:59 -0600
commita172a2362cb158bee6f866fbc538cc04642c4f7a (patch)
tree90196fcc2c40a9ccf0b4a0d9958f063861df3223
parent1c84e423adee8f4447a53ac39abffc9767da531f (diff)
downloadnuttx-a172a2362cb158bee6f866fbc538cc04642c4f7a.tar.gz
nuttx-a172a2362cb158bee6f866fbc538cc04642c4f7a.tar.bz2
nuttx-a172a2362cb158bee6f866fbc538cc04642c4f7a.zip
SAM4E-EK: Add an EMAC driver. Initial commit is just a quick port of the SAMA5D3 EMAC driver
-rw-r--r--nuttx/arch/arm/src/sam34/Kconfig218
-rw-r--r--nuttx/arch/arm/src/sam34/Make.defs4
-rw-r--r--nuttx/arch/arm/src/sam34/sam4e_periphclks.h4
-rw-r--r--nuttx/arch/arm/src/sam34/sam_cmcc.h7
-rw-r--r--nuttx/arch/arm/src/sam34/sam_emac.c2917
-rw-r--r--nuttx/arch/arm/src/sam34/sam_emac.h120
-rw-r--r--nuttx/configs/sam4e-ek/README.txt176
-rw-r--r--nuttx/net/Kconfig4
8 files changed, 3429 insertions, 21 deletions
diff --git a/nuttx/arch/arm/src/sam34/Kconfig b/nuttx/arch/arm/src/sam34/Kconfig
index e04e609ba..fcd08a52b 100644
--- a/nuttx/arch/arm/src/sam34/Kconfig
+++ b/nuttx/arch/arm/src/sam34/Kconfig
@@ -349,6 +349,8 @@ config SAM34_EMAC
bool "Ethernet MAC (EMAC)"
default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4E
+ select NETDEVICES
+ select ARCH_HAVE_PHY
config SAM34_FREQM
bool "Frequency Meter (FREQM)"
@@ -688,7 +690,7 @@ config SAM34_RC32K
endmenu # AT91SAM3/4 Clock Configuration
endif # ARCH_CHIP_SAM4L
-menu "External Memory Configuration"
+menu "AT91SAM3/4 External Memory Configuration"
config ARCH_HAVE_EXTNAND
bool
@@ -830,7 +832,7 @@ config SAM34_EXTSRAM1HEAP
endif # SAM34_EXTSRAM1
endmenu # External Memory Configuration
-comment "AT91SAM3/4 GPIO Interrupt Configuration"
+menu "AT91SAM3/4 GPIO Interrupt Configuration"
config GPIO_IRQ
bool "GPIO pin interrupts"
@@ -867,6 +869,218 @@ config GPIOF_IRQ
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A
endif # GPIO_IRQ
+endmenu # AT91SAM3/4 GPIO Interrupt Configuration
+
+if SAM34_EMAC
+
+menu "AT91SAM3/4 EMAC device driver options"
+
+config SAM34_EMAC_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 SAM34_EMAC_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 SAM34_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 SAM34_EMAC_NBC
+ bool "Disable Broadcast"
+ default n
+ ---help---
+ Select to disable receipt of broadcast packets.
+
+config SAM34_EMAC_PHYADDR
+ int "PHY address"
+ default 1
+ ---help---
+ The 5-bit address of the PHY on the board. Default: 1
+
+config SAM34_EMAC_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
+ SAM34_EMAC_PHYINIT is defined in the configuration then the board specific logic must
+ provide sam_phyinitialize(); The SAM34 EMAC driver will call this function
+ one time before it first uses the PHY.
+
+choice
+ prompt "PHY interface"
+ default SAM34_EMAC_MII
+
+config SAM34_EMAC_MII
+ bool "MII"
+ ---help---
+ Support Ethernet MII interface (vs RMII).
+
+config SAM34_EMAC_RMII
+ bool "RMII"
+ depends on !ARCH_CHIP_SAM4E
+ ---help---
+ Support Ethernet RMII interface (vs MII).
+
+endchoice # PHY interface
+
+config SAM34_EMAC_CLAUSE45
+ bool "Clause 45 MII"
+ depends on SAM34_EMAC_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 SAM34_EMAC_AUTONEG
+ bool "Use autonegotiation"
+ default y
+ ---help---
+ Use PHY autonegotiation to determine speed and mode
+
+config SAM34_EMAC_ETHFD
+ bool "Full duplex"
+ default n
+ depends on !SAM34_EMAC_AUTONEG
+ ---help---
+ If SAM34_EMAC_AUTONEG is not defined, then this may be defined to select full duplex
+ mode. Default: half-duplex
+
+config SAM34_EMAC_ETH100MBPS
+ bool "100 Mbps"
+ default n
+ depends on !SAM34_EMAC_AUTONEG
+ ---help---
+ If SAM34_EMAC_AUTONEG is not defined, then this may be defined to select 100 MBps
+ speed. Default: 10 Mbps
+
+config SAM34_EMAC_PHYSR
+ int "PHY Status Register Address (decimal)"
+ depends on SAM34_EMAC_AUTONEG
+ ---help---
+ This must be provided if SAM34_EMAC_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 SAM34_EMAC_PHYSR_ALTCONFIG
+ bool "PHY Status Alternate Bit Layout"
+ default n
+ depends on SAM34_EMAC_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.
+
+config SAM34_EMAC_PHYSR_SPEED
+ hex "PHY Speed Mask"
+ depends on SAM34_EMAC_AUTONEG && !SAM34_EMAC_PHYSR_ALTCONFIG
+ ---help---
+ This must be provided if SAM34_EMAC_AUTONEG is defined. This provides bit mask
+ for isolating the 10 or 100MBps speed indication.
+
+config SAM34_EMAC_PHYSR_100MBPS
+ hex "PHY 100Mbps Speed Value"
+ depends on SAM34_EMAC_AUTONEG && !SAM34_EMAC_PHYSR_ALTCONFIG
+ ---help---
+ This must be provided if SAM34_EMAC_AUTONEG is defined. This provides the value
+ of the speed bit(s) indicating 100MBps speed.
+
+config SAM34_EMAC_PHYSR_MODE
+ hex "PHY Mode Mask"
+ depends on SAM34_EMAC_AUTONEG && !SAM34_EMAC_PHYSR_ALTCONFIG
+ ---help---
+ This must be provided if SAM34_EMAC_AUTONEG is defined. This provide bit mask
+ for isolating the full or half duplex mode bits.
+
+config SAM34_EMAC_PHYSR_FULLDUPLEX
+ hex "PHY Full Duplex Mode Value"
+ depends on SAM34_EMAC_AUTONEG && !SAM34_EMAC_PHYSR_ALTCONFIG
+ ---help---
+ This must be provided if SAM34_EMAC_AUTONEG is defined. This provides the
+ value of the mode bits indicating full duplex mode.
+
+config SAM34_EMAC_PHYSR_ALTMODE
+ hex "PHY Mode Mask"
+ depends on SAM34_EMAC_AUTONEG && SAM34_EMAC_PHYSR_ALTCONFIG
+ ---help---
+ This must be provided if SAM34_EMAC_AUTONEG is defined. This provide bit mask
+ for isolating the speed and full/half duplex mode bits.
+
+config SAM34_EMAC_PHYSR_10HD
+ hex "10MBase-T Half Duplex Value"
+ depends on SAM34_EMAC_AUTONEG && SAM34_EMAC_PHYSR_ALTCONFIG
+ ---help---
+ This must be provided if SAM34_EMAC_AUTONEG is defined. This is the value
+ under the bit mask that represents the 10Mbps, half duplex setting.
+
+config SAM34_EMAC_PHYSR_100HD
+ hex "100Base-T Half Duplex Value"
+ depends on SAM34_EMAC_AUTONEG && SAM34_EMAC_PHYSR_ALTCONFIG
+ ---help---
+ This must be provided if SAM34_EMAC_AUTONEG is defined. This is the value
+ under the bit mask that represents the 100Mbps, half duplex setting.
+
+config SAM34_EMAC_PHYSR_10FD
+ hex "10Base-T Full Duplex Value"
+ depends on SAM34_EMAC_AUTONEG && SAM34_EMAC_PHYSR_ALTCONFIG
+ ---help---
+ This must be provided if SAM34_EMAC_AUTONEG is defined. This is the value
+ under the bit mask that represents the 10Mbps, full duplex setting.
+
+config SAM34_EMAC_PHYSR_100FD
+ hex "100Base-T Full Duplex Value"
+ depends on SAM34_EMAC_AUTONEG && SAM34_EMAC_PHYSR_ALTCONFIG
+ ---help---
+ This must be provided if SAM34_EMAC_AUTONEG is defined. This is the value
+ under the bit mask that represents the 100Mbps, full duplex setting.
+
+config SAM34_EMAC_REGDEBUG
+ bool "Register-Level Debug"
+ default n
+ depends on DEBUG
+ ---help---
+ Enable very low-level register access debug. Depends on DEBUG.
+
+config SAM34_EMAC_ISETH0
+ bool
+ default y if !SAM34_EMAC || !SAM34_GMAC_ISETH0
+ default n if SAM34_EMAC && SAM34_GMAC_ISETH0
+
+endmenu # EMAC device driver options
+endif # SAM34_EMAC
if SAM34_WDT
comment "AT91SAM3/4 Watchdog Configuration"|
diff --git a/nuttx/arch/arm/src/sam34/Make.defs b/nuttx/arch/arm/src/sam34/Make.defs
index 0fe0d5b56..c645319c3 100644
--- a/nuttx/arch/arm/src/sam34/Make.defs
+++ b/nuttx/arch/arm/src/sam34/Make.defs
@@ -104,6 +104,10 @@ ifeq ($(CONFIG_SAM34_PDCA),y)
CHIP_CSRCS += sam4l_pdca.c
endif
+ifeq ($(CONFIG_SAM34_EMAC),y)
+CHIP_CSRCS += sam_emac.c
+endif
+
ifeq ($(CONFIG_SAM34_HSMCI),y)
CHIP_CSRCS += sam_hsmci.c
endif
diff --git a/nuttx/arch/arm/src/sam34/sam4e_periphclks.h b/nuttx/arch/arm/src/sam34/sam4e_periphclks.h
index 14d96a02a..5f41fee35 100644
--- a/nuttx/arch/arm/src/sam34/sam4e_periphclks.h
+++ b/nuttx/arch/arm/src/sam34/sam4e_periphclks.h
@@ -52,8 +52,8 @@
#define sam_enableperiph0(s) putreg32((1 << (s)), SAM_PMC_PCER0)
#define sam_enableperiph1(s) putreg32((1 << ((s) - 32)), SAM_PMC_PCER1)
-#define sam_disableperiph0(s) putreg32((1 << (s)), SAM_PMC_PDER0)
-#define sam_disableperiph1(s) putreg32((1 << ((s) - 32)), SAM_PMC_PDER1)
+#define sam_disableperiph0(s) putreg32((1 << (s)), SAM_PMC_PCDR0)
+#define sam_disableperiph1(s) putreg32((1 << ((s) - 32)), SAM_PMC_PCDR1)
#define sam_supc_enableclk() sam_enableperiph0(SAM_PID_SUPC)
#define sam_rstc_enableclk() sam_enableperiph0(SAM_PID_RSTC)
diff --git a/nuttx/arch/arm/src/sam34/sam_cmcc.h b/nuttx/arch/arm/src/sam34/sam_cmcc.h
index 6b7f17ca5..6593ef6fe 100644
--- a/nuttx/arch/arm/src/sam34/sam_cmcc.h
+++ b/nuttx/arch/arm/src/sam34/sam_cmcc.h
@@ -125,5 +125,12 @@ void sam_cmcc_invalidateall(void);
#endif
#endif /* __ASSEMBLY__ */
+#else /* CONFIG_SAM34_CMCC */
+
+/* Stubs so that we don't have to put condition compilation in driver source */
+
+# define sam_cmcc_invalidate(start, end)
+# define sam_cmcc_invalidateall()
+
#endif /* CONFIG_SAM34_CMCC */
#endif /* __ARCH_ARM_SRC_SAM34_SAM_CMCC_H */
diff --git a/nuttx/arch/arm/src/sam34/sam_emac.c b/nuttx/arch/arm/src/sam34/sam_emac.c
new file mode 100644
index 000000000..e9f965718
--- /dev/null
+++ b/nuttx/arch/arm/src/sam34/sam_emac.c
@@ -0,0 +1,2917 @@
+/****************************************************************************
+ * arch/arm/src/sam34/sam_emac.c
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * This logic derives from the SAM34D3 Ethernet driver.
+ *
+ * Copyright (C) 2013 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <string.h>
+#include <debug.h>
+#include <wdog.h>
+#include <queue.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arp.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+#include "up_arch.h"
+#include "up_internal.h"
+
+#include "chip.h"
+#include "chip/sam_pinmap.h"
+#include "sam_gpio.h"
+#include "sam_periphclks.h"
+#include "sam_cmcc.h"
+#include "sam_emac.h"
+
+#include <arch/board/board.h>
+
+#if defined(CONFIG_NET) && defined(CONFIG_SAM34_EMAC)
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+
+/* Number of buffer for RX */
+
+#ifndef CONFIG_SAM34_EMAC_NRXBUFFERS
+# define CONFIG_SAM34_EMAC_NRXBUFFERS 16
+#endif
+
+/* Number of buffer for TX */
+
+#ifndef CONFIG_SAM34_EMAC_NTXBUFFERS
+# define CONFIG_SAM34_EMAC_NTXBUFFERS 8
+#endif
+
+#undef CONFIG_SAM34_EMAC_NBC
+
+#ifndef CONFIG_SAM34_EMAC_PHYADDR
+# error "CONFIG_SAM34_EMAC_PHYADDR must be defined in the NuttX configuration"
+#endif
+
+#if !defined(CONFIG_SAM34_EMAC_MII) && !defined(CONFIG_SAM34_EMAC_RMII)
+# warning "Neither CONFIG_SAM34_EMAC_MII nor CONFIG_SAM34_EMAC_RMII defined"
+#endif
+
+#if defined(CONFIG_SAM34_EMAC_MII) && defined(CONFIG_SAM34_EMAC_RMII)
+# error "Both CONFIG_SAM34_EMAC_MII and CONFIG_SAM34_EMAC_RMII defined"
+#endif
+
+#ifdef CONFIG_SAM34_EMAC_AUTONEG
+# ifndef CONFIG_SAM34_EMAC_PHYSR
+# error "CONFIG_SAM34_EMAC_PHYSR must be defined in the NuttX configuration"
+# endif
+# ifdef CONFIG_SAM34_EMAC_PHYSR_ALTCONFIG
+# ifndef CONFIG_SAM34_EMAC_PHYSR_ALTMODE
+# error "CONFIG_SAM34_EMAC_PHYSR_ALTMODE must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAM34_EMAC_PHYSR_10HD
+# error "CONFIG_SAM34_EMAC_PHYSR_10HD must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAM34_EMAC_PHYSR_100HD
+# error "CONFIG_SAM34_EMAC_PHYSR_100HD must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAM34_EMAC_PHYSR_10FD
+# error "CONFIG_SAM34_EMAC_PHYSR_10FD must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAM34_EMAC_PHYSR_100FD
+# error "CONFIG_SAM34_EMAC_PHYSR_100FD must be defined in the NuttX configuration"
+# endif
+# else
+# ifndef CONFIG_SAM34_EMAC_PHYSR_SPEED
+# error "CONFIG_SAM34_EMAC_PHYSR_SPEED must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAM34_EMAC_PHYSR_100MBPS
+# error "CONFIG_SAM34_EMAC_PHYSR_100MBPS must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAM34_EMAC_PHYSR_MODE
+# error "CONFIG_SAM34_EMAC_PHYSR_MODE must be defined in the NuttX configuration"
+# endif
+# ifndef CONFIG_SAM34_EMAC_PHYSR_FULLDUPLEX
+# error "CONFIG_SAM34_EMAC_PHYSR_FULLDUPLEX must be defined in the NuttX configuration"
+# endif
+# endif
+#endif
+
+/* PHY definitions */
+
+#if defined(CONFIG_ETH0_PHY_DM9161)
+# define MII_OUI_MSB 0x0181
+# define MII_OUI_LSB 0x2e
+#elif defined(CONFIG_ETH0_PHY_LAN8700)
+# define MII_OUI_MSB 0x0007
+# define MII_OUI_LSB 0x30
+#elif defined(CONFIG_ETH0_PHY_KSZ8051)
+# define MII_OUI_MSB 0x0022
+# define MII_OUI_LSB 0x05
+#else
+# error EMAC PHY unrecognized
+#endif
+
+#ifdef CONFIG_SAM34_EMAC_PHYSR_ALTCONFIG
+
+# define PHYSR_MODE(sr) ((sr) & CONFIG_SAM34_EMAC_PHYSR_ALTMODE)
+# define PHYSR_ISMODE(sr,m) (PHYSR_MODE(sr) == (m))
+
+# define PHYSR_IS10HDX(sr) PHYSR_ISMODE(sr,CONFIG_SAM34_EMAC_PHYSR_10HD)
+# define PHYSR_IS100HDX(sr) PHYSR_ISMODE(sr,CONFIG_SAM34_EMAC_PHYSR_100HD)
+# define PHYSR_IS10FDX(sr) PHYSR_ISMODE(sr,CONFIG_SAM34_EMAC_PHYSR_10FD)
+# define PHYSR_IS100FDX(sr) PHYSR_ISMODE(sr,CONFIG_SAM34_EMAC_PHYSR_100FD)
+
+#else
+
+# define PHYSR_MODESPEED (CONFIG_PHYSR_MODE | CONFIG_PHYSR_SPEED)
+# define PHYSR_10HDX (0)
+# define PHYSR_100HDX (CONFIG_PHYSR_100MBPS)
+# define PHYSR_10FDX (CONFIG_PHYSR_FULLDUPLEX)
+# define PHYSR_100FDX (CONFIG_PHYSR_FULLDUPLEX | CONFIG_PHYSR_100MBPS)
+
+# define PHYSR_IS10HDX(sr) (((sr) & PHYSR_MODESPEED) == PHYSR_10HDX)
+# define PHYSR_IS100HDX(sr) (((sr) & PHYSR_MODESPEED) == PHYSR_100HDX)
+# define PHYSR_IS10FDX(sr) (((sr) & PHYSR_MODESPEED) == PHYSR_10FDX)
+# define PHYSR_IS100FDX(sr) (((sr) & PHYSR_MODESPEED) == PHYSR_100FDX)
+
+#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_BUFSIZE /* MAX size for Ethernet packet */
+
+/* We need at least one more free buffer than transmit buffers */
+
+#define SAM_EMAC_NFREEBUFFERS (CONFIG_SAM34_EMAC_NTXBUFFERS+1)
+
+/* Extremely detailed register debug that you would normally never want
+ * enabled.
+ */
+
+#ifndef CONFIG_DEBUG
+# undef CONFIG_SAM34_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
+
+/* 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 uip_eth_hdr *)priv->dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* 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 */
+
+ /* This holds the information visible to uIP/NuttX */
+
+ struct uip_driver_s dev; /* Interface understood by uIP */
+
+ /* Used to track transmit and receive descriptors */
+
+ uint8_t phyaddr; /* PHY address (pre-defined by pins on reset) */
+ uint16_t txhead; /* Circular buffer head index */
+ uint16_t txtail; /* Circualr 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_SAM34_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 Data
+ ****************************************************************************/
+
+/* The driver state singleton */
+
+static struct sam_emac_s g_emac;
+
+#ifdef CONFIG_SAM34_EMAC_PREALLOCATE
+/* Preallocated data */
+/* TX descriptors list */
+
+static struct emac_txdesc_s g_txdesc[CONFIG_SAM34_EMAC_NTXBUFFERS]
+ __attribute__((aligned(8)));
+
+/* RX descriptors list */
+
+static struct emac_rxdesc_s g_rxdesc[CONFIG_SAM34_EMAC_NRXBUFFERS]
+ __attribute__((aligned(8)));
+
+/* 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_txbuffer[CONFIG_SAM34_EMAC_NTXBUFFERS * EMAC_TX_UNITSIZE];
+ __attribute__((aligned(8)))
+
+/* Receive Buffers */
+
+static uint8_t g_rxbuffer[CONFIG_SAM34_EMAC_NRXBUFFERS * EMAC_RX_UNITSIZE]
+ __attribute__((aligned(8)));
+
+#endif
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* Register operations ******************************************************/
+
+#if defined(CONFIG_SAM34_EMAC_REGDEBUG) && defined(CONFIG_DEBUG)
+static bool sam_checkreg(struct sam_emac_s *priv, bool wr,
+ uint32_t regval, uintptr_t address);
+static uint32_t sam_getreg(struct sam_emac_s *priv, uintptr_t addr);
+static void sam_putreg(struct sam_emac_s *priv, uintptr_t addr, uint32_t val);
+#else
+# define sam_getreg(priv,addr) getreg32(addr)
+# define sam_putreg(priv,addr,val) putreg32(val,addr)
+#endif
+
+/* 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_uiptxpoll(struct uip_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 int sam_emac_interrupt(int irq, void *context);
+
+/* Watchdog timer expirations */
+
+static void sam_polltimer(int argc, uint32_t arg, ...);
+static void sam_txtimeout(int argc, uint32_t arg, ...);
+
+/* NuttX callback functions */
+
+static int sam_ifup(struct uip_driver_s *dev);
+static int sam_ifdown(struct uip_driver_s *dev);
+static int sam_txavail(struct uip_driver_s *dev);
+#ifdef CONFIG_NET_IGMP
+static int sam_addmac(struct uip_driver_s *dev, const uint8_t *mac);
+static int sam_rmmac(struct uip_driver_s *dev, const uint8_t *mac);
+#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
+
+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_reset(struct sam_emac_s *priv);
+static void sam_macaddress(struct sam_emac_s *priv);
+static int sam_emac_configure(struct sam_emac_s *priv);
+
+/****************************************************************************
+ * 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_SAM34_EMAC_REGDEBUG
+static bool sam_checkreg(struct sam_emac_s *priv, bool wr, uint32_t regval,
+ uintptr_t address)
+{
+ if (wr == priv->wrlast && /* Same kind of access? */
+ regval == priv->vallast && /* Same value? */
+ address == 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 = address;
+ 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 any 32-bit register using an absolute
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAM34_EMAC_REGDEBUG
+static uint32_t sam_getreg(struct sam_emac_s *priv, uintptr_t address)
+{
+ uint32_t regval = getreg32(address);
+
+ if (sam_checkreg(priv, false, regval, address))
+ {
+ lldbg("%08x->%08x\n", address, regval);
+ }
+
+ return regval;
+}
+#endif
+
+/****************************************************************************
+ * Name: sam_putreg
+ *
+ * Description:
+ * Write to any 32-bit register using an absolute address
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAM34_EMAC_REGDEBUG
+static void sam_putreg(struct sam_emac_s *priv, uintptr_t address,
+ uint32_t regval)
+{
+ if (sam_checkreg(priv, true, regval, address))
+ {
+ lldbg("%08x<-%08x\n", address, regval);
+ }
+
+ putreg32(regval, address);
+}
+#endif
+
+/****************************************************************************
+ * 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)
+ {
+ return txhead32 += CONFIG_SAM34_EMAC_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 (CONFIG_SAM34_EMAC_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_SAM34_EMAC_PREALLOCATE
+ /* Use pre-allocated buffers */
+
+ priv->txdesc = g_txdesc;
+ priv->rxdesc = g_rxdesc;
+ priv->txbuffer = g_txbuffer;
+ priv->rxbuffer = g_rxbuffer;
+
+#else
+ size_t allocsize;
+
+ /* Allocate buffers */
+
+ allocsize = CONFIG_SAM34_EMAC_NTXBUFFERS * sizeof(struct emac_txdesc_s);
+ priv->txdesc = (struct emac_txdesc_s *)kmemalign(8, allocsize);
+ if (!priv->txdesc)
+ {
+ nlldbg("ERROR: Failed to allocate TX descriptors\n");
+ return -ENOMEM;
+ }
+
+ memset(priv->txdesc, 0, allocsize);
+
+ allocsize = CONFIG_SAM34_EMAC_NRXBUFFERS * sizeof(struct emac_rxdesc_s);
+ priv->rxdesc = (struct emac_rxdesc_s *)kmemalign(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 = CONFIG_SAM34_EMAC_NTXBUFFERS * EMAC_TX_UNITSIZE;
+ priv->txbuffer = (uint8_t *)kmemalign(8, allocsize);
+ if (!priv->txbuffer)
+ {
+ nlldbg("ERROR: Failed to allocate TX buffer\n");
+ sam_buffer_free(priv);
+ return -ENOMEM;
+ }
+
+ allocsize = CONFIG_SAM34_EMAC_NRXBUFFERS * EMAC_RX_UNITSIZE;
+ priv->rxbuffer = (uint8_t *)kmemalign(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_SAM34_EMAC_PREALLOCATE
+ /* Free allocated buffers */
+
+ if (priv->txdesc)
+ {
+ kfree(priv->txdesc);
+ priv->txdesc = NULL;
+ }
+
+ if (priv->rxdesc)
+ {
+ kfree(priv->rxdesc);
+ priv->rxdesc = NULL;
+ }
+
+ if (priv->txbuffer)
+ {
+ kfree(priv->txbuffer);
+ priv->txbuffer = NULL;
+ }
+
+ if (priv->rxbuffer)
+ {
+ kfree(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 uip_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);
+ }
+
+ /* Update TX descriptor status. */
+
+ status = dev->d_len | EMACTXD_STA_LAST;
+ if (priv->txhead == CONFIG_SAM34_EMAC_NTXBUFFERS-1)
+ {
+ status |= EMACTXD_STA_WRAP;
+ }
+
+ /* Update the descriptor status */
+
+ txdesc->status = status;
+
+ /* Increment the head index */
+
+ if (++priv->txhead >= CONFIG_SAM34_EMAC_NTXBUFFERS)
+ {
+ priv->txhead = 0;
+ }
+
+ /* Now start transmission (if it is not already done) */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR);
+ regval |= EMAC_NCR_TSTART;
+ sam_putreg(priv, SAM_EMAC_NCR, regval);
+
+ /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+ (void)wd_start(priv->txtimeout, SAM_TXTIMEOUT, sam_txtimeout, 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 dispatch is also an opportunity to replay with the a TX
+ * packet. So, if we cannot handle an RX packet replay, then we disable
+ * all RX packet processing.
+ */
+
+ if (sam_txfree(priv) < 1)
+ {
+ nllvdbg("Disabling RX interrupts\n");
+ sam_putreg(priv, SAM_EMAC_IDR, EMAC_INT_RCOMP);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: sam_uiptxpoll
+ *
+ * Description:
+ * The transmitter is available, check if uIP has any outgoing packets ready
+ * to send. This is a callback from uip_poll(). uip_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete,
+ * 2. When the preceding TX packet send 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_uiptxpoll(struct uip_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)
+ {
+ /* Send the packet */
+
+ uip_arp_out(&priv->dev);
+ 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 uip_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)uip_poll(dev, sam_uiptxpoll);
+ }
+}
+
+/****************************************************************************
+ * 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 uip_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;
+
+ rxndx = priv->rxndx;
+ rxdesc = &priv->rxdesc[rxndx];
+ isframe = false;
+
+ /* Invalidate the RX descriptor to force re-fetching from RAM */
+
+ sam_cmcc_invalidate((uintptr_t)rxdesc,
+ (uintptr_t)rxdesc + sizeof(struct emac_rxdesc_s));
+
+ 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);
+
+ /* Increment the RX index */
+
+ if (++priv->rxndx >= CONFIG_SAM34_EMAC_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 >= CONFIG_SAM34_EMAC_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);
+
+ /* Increment the RX index */
+
+ if (++priv->rxndx >= CONFIG_SAM34_EMAC_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_BUFSIZE)
+ {
+ copylen = CONFIG_NET_BUFSIZE - pktlen;
+ }
+
+ /* Get the data source. Invalidate the source memory region to
+ * force reload from RAM.
+ */
+
+ src = (const uint8_t *)(rxdesc->addr & EMACRXD_ADDR_MASK);
+ sam_cmcc_invalidate((uintptr_t)src, (uintptr_t)src + copylen);
+
+ /* 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);
+
+ /* Increment the RX index */
+
+ if (++priv->rxndx >= CONFIG_SAM34_EMAC_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 encount the SOF yet... discard this fragment and keep looking */
+
+ else
+ {
+ /* Give ownership back to the EMAC */
+
+ rxdesc->addr &= ~(EMACRXD_ADDR_OWNER);
+ priv->rxndx = rxndx;
+ }
+
+ /* Process the next buffer */
+
+ rxdesc = &priv->rxdesc[rxndx];
+
+ /* Invalidate the RX descriptor to force re-fetching from RAM */
+
+ sam_cmcc_invalidate((uintptr_t)rxdesc,
+ (uintptr_t)rxdesc + sizeof(struct emac_rxdesc_s));
+ }
+
+ /* 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 a new RX packet
+ * 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 uip_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_BUFSIZE)
+ {
+ nlldbg("DROPPED: Too big: %d\n", dev->d_len);
+ }
+
+ /* We only accept IP packets of the configured type and ARP packets */
+
+#ifdef CONFIG_NET_IPv6
+ else if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
+#else
+ else if (BUF->type == HTONS(UIP_ETHTYPE_IP))
+#endif
+ {
+ nllvdbg("IP frame\n");
+
+ /* Handle ARP on input then give the IP packet to uIP */
+
+ uip_arp_ipin(&priv->dev);
+ uip_input(&priv->dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ uip_arp_out(&priv->dev);
+ sam_transmit(priv);
+ }
+ }
+ else if (BUF->type == htons(UIP_ETHTYPE_ARP))
+ {
+ nllvdbg("ARP frame\n");
+
+ /* Handle ARP packet */
+
+ uip_arp_arpin(&priv->dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, the field d_len will set to a value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ sam_transmit(priv);
+ }
+ }
+ else
+ {
+ nlldbg("DROPPED: Unknown type: %04x\n", BUF->type);
+ }
+ }
+}
+
+/****************************************************************************
+ * Function: sam_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that a frame has 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;
+
+ /* Are there any outstanding transmssions? Loop until either (1) all of
+ * the TX 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];
+ sam_cmcc_invalidate((uintptr_t)txdesc,
+ (uintptr_t)txdesc + sizeof(struct emac_txdesc_s));
+
+ /* Is this TX descriptor still in use? */
+
+ if ((txdesc->status & EMACTXD_STA_USED) == 0)
+ {
+ /* 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))
+ {
+ txdesc->status = (uint32_t)EMACTXD_STA_USED;
+ }
+ else
+#endif
+ {
+ /* Otherwise, the descriptor is truly in use. Break out of the
+ * loop now.
+ */
+
+ break;
+ }
+ }
+
+ /* Increment the tail index */
+
+ if (++priv->txtail >= CONFIG_SAM34_EMAC_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 desciptors (see commits in sam_transmit()).
+ */
+
+ sam_putreg(priv, SAM_EMAC_IER, EMAC_INT_RCOMP);
+ }
+
+ /* Then poll uIP for new XMIT data */
+
+ sam_dopoll(priv);
+}
+
+/****************************************************************************
+ * Function: sam_emac_interrupt
+ *
+ * Description:
+ * Hardware interrupt handler
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int sam_emac_interrupt(int irq, void *context)
+{
+ struct sam_emac_s *priv = &g_emac;
+ 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);
+ rsr = sam_getreg(priv, SAM_EMAC_RSR);
+ tsr = sam_getreg(priv, SAM_EMAC_TSR);
+ imr = sam_getreg(priv, SAM_EMAC_IMR);
+
+ 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);
+ regval |= EMAC_NCR_TXEN;
+ sam_putreg(priv, SAM_EMAC_NCR, 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;
+ }
+
+ /* Check for Transmit Underrun (UND)
+ *
+ * ISR:UND is set transmit DMA was not able to read data from memory,
+ * either because the bus was not granted in time, because a not
+ * OK hresp(bus error) was returned or because a used bit was read
+ * midway through frame transmission. If this occurs, the
+ * transmitter forces bad CRC. Cleared by writing a one to this bit.
+ */
+
+ if ((tsr & EMAC_TSR_UND) != 0)
+ {
+ nlldbg("ERROR: Transmit Underrun TSR: %08x\n", tsr);
+ clrbits |= EMAC_TSR_UND;
+ }
+
+ /* Clear status */
+
+ sam_putreg(priv, SAM_EMAC_TSR, 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 whent he 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, clrbits);
+
+ /* Handle the received packet */
+
+ sam_receive(priv);
+ }
+
+#ifdef CONFIG_DEBUG_NET
+ /* Check for PAUSE Frame recieved (PFRE).
+ *
+ * ISR:PFRE indicates that a pause frame has been received with non-zero
+ * pause quantum. Cleared on a read.
+ */
+
+ if ((pending & EMAC_INT_PFNX) != 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
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: sam_txtimeout
+ *
+ * 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(int argc, uint32_t arg, ...)
+{
+ struct sam_emac_s *priv = (struct sam_emac_s *)arg;
+
+ nlldbg("Timeout!\n");
+
+ /* Then 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_polltimer
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void sam_polltimer(int argc, uint32_t arg, ...)
+{
+ struct sam_emac_s *priv = (struct sam_emac_s *)arg;
+ struct uip_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)uip_timer(dev, sam_uiptxpoll, SAM_POLLHSEC);
+ }
+
+ /* Setup the watchdog poll timer again */
+
+ (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_polltimer, 1, arg);
+}
+
+/****************************************************************************
+ * 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 uip_driver_s *dev)
+{
+ struct sam_emac_s *priv = (struct sam_emac_s *)dev->d_private;
+ int ret;
+
+ nlldbg("Bringing up: %d.%d.%d.%d\n",
+ dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
+ (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24);
+
+ /* 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);
+
+ /* 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_polltimer, 1, (uint32_t)priv);
+
+ /* Enable the EMAC interrupt */
+
+ priv->ifup = true;
+ up_enable_irq(SAM_IRQ_EMAC);
+ 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 uip_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(SAM_IRQ_EMAC);
+
+ /* 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
+ *
+ * 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 uip_driver_s *dev)
+{
+ struct sam_emac_s *priv = (struct sam_emac_s *)dev->d_private;
+ irqstate_t flags;
+
+ nllvdbg("ifup: %d\n", priv->ifup);
+
+ /* Disable interrupts because this function may be called from interrupt
+ * level processing.
+ */
+
+ flags = irqsave();
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (priv->ifup)
+ {
+ /* Poll uIP for new XMIT data */
+
+ sam_dopoll(priv);
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * 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:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_IGMP
+static int sam_addmac(struct uip_driver_s *dev, const uint8_t *mac)
+{
+ struct sam_emac_s *priv = (struct sam_emac_s *)dev->d_private;
+
+ nllvdbg("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+ /* Add the MAC address to the hardware multicast routing table */
+ /* Add the MAC address to the hardware multicast routing table */
+#error "Missing logic"
+
+ 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 uip_driver_s *dev, const uint8_t *mac)
+{
+ struct sam_emac_s *priv = (struct sam_emac_s *)dev->d_private;
+
+ nllvdbg("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+ /* Add the MAC address to the hardware multicast routing table */
+#error "Missing logic"
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * 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);
+ regval |= EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR, regval);
+
+#ifdef CONFIG_SAM34_EMAC_RMII
+ nllvdbg("RMII Registers (Address %02x)\n", priv->phyaddr);
+#else /* defined(CONFIG_SAM34_EMAC_MII) */
+ nllvdbg("MII Registers (Address %02x)\n", priv->phyaddr);
+#endif
+
+ 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, CONFIG_SAM34_EMAC_PHYSR, &phyval);
+ nllvdbg(" PHYSR: %04x\n", phyval);
+
+ /* Disable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR);
+ regval &= ~EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR, regval);
+}
+#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) & 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);
+ regval |= EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR, 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);
+ regval &= ~EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR, 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);
+ regval |= EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR, regval);
+
+ candidate = *phyaddr;
+
+ /* Check current candidate address */
+
+ ret = sam_phyread(priv, candidate, MII_PHYID1, &phyval);
+ if (ret == OK && phyval == MII_OUI_MSB)
+ {
+ *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 == MII_OUI_MSB)
+ {
+ ret = OK;
+ break;
+ }
+ }
+ }
+
+ if (ret == OK)
+ {
+ nllvdbg(" PHYID1: %04x PHY addr: %d\n", phyval, candidate);
+ *phyaddr = candidate;
+ sam_phyread(priv, candidate, CONFIG_SAM34_EMAC_PHYSR, &phyval);
+ nllvdbg(" PHYSR: %04x PHY addr: %d\n", phyval, candidate);
+ }
+
+ /* Disable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR);
+ regval &= ~EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR, 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;
+
+#ifndef CONFIG_SAM34_EMAC_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;
+#endif
+
+ sam_putreg(priv, SAM_EMAC_MAN, 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) & 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;
+
+#ifndef CONFIG_SAM34_EMAC_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;
+#endif
+
+ sam_putreg(priv, SAM_EMAC_MAN, 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);
+ regval |= EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR, 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 == MII_OUI_MSB &&
+ ((phyid2 & MII_PHYID2_OUI_MASK) >> MII_PHYID2_OUI_SHIFT) == MII_OUI_LSB)
+ {
+ 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);
+ 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, regval);
+
+ /* Select RMII/MII */
+
+ regval = sam_getreg(priv, SAM_EMAC_UR);
+#ifdef CONFIG_SAM34_EMAC_RMII
+ regval &= ~EMAC_UR_MII;
+#else /* defined(CONFIG_SAM34_EMAC_MII) */
+ regval |= EMAC_UR_MII;
+#endif
+ sam_putreg(priv, SAM_EMAC_UR, regval);
+
+errout:
+ /* Disable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR);
+ regval &= ~EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR, 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);
+ regval |= EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR, 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, CONFIG_SAM34_EMAC_PHYSR, &physr);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to read PHYSR: %d\n", ret);
+ goto errout;
+ }
+
+ regval = sam_getreg(priv, SAM_EMAC_NCFGR);
+ regval &= ~(EMAC_NCFGR_SPD | EMAC_NCFGR_FD);
+
+ if ((msr & MII_MSR_100BASETXFULL) != 0 && PHYSR_IS100FDX(physr))
+ {
+ /* Set EMAC for 100BaseTX and Full Duplex */
+
+ regval |= (EMAC_NCFGR_SPD | EMAC_NCFGR_FD);
+ }
+ else if ((msr & MII_MSR_10BASETXFULL) != 0 && PHYSR_IS10FDX(physr))
+ {
+ /* Set MII for 10BaseT and Full Duplex */
+
+ regval |= EMAC_NCFGR_FD;
+ }
+
+ else if ((msr & MII_MSR_100BASETXHALF) != 0 && PHYSR_IS100HDX(physr))
+ {
+ /* Set MII for 100BaseTX and Half Duplex */
+
+ regval |= EMAC_NCFGR_SPD;
+ }
+
+#if 0
+ else if ((msr & MII_MSR_10BASETXHALF) != 0 && PHYSR_IS10HDX(physr))
+ {
+ /* Set MII for 10BaseT and Half Duplex */
+ }
+#endif
+
+ sam_putreg(priv, SAM_EMAC_NCFGR, regval);
+
+ /* Start the EMAC transfers */
+
+ nllvdbg("Link is up\n");
+ linkup = true;
+
+errout:
+ /* Disable management port */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR);
+ regval &= ~EMAC_NCR_MPE;
+ sam_putreg(priv, SAM_EMAC_NCR, 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;
+ int ret;
+
+ /* Configure PHY clocking */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCFGR);
+ regval &= ~EMAC_NCFGR_CLK_MASK;
+
+#if BOARD_MCK_FREQUENCY > (160*1000*1000)
+# error Supported MCK frequency
+#elif BOARD_MCK_FREQUENCY > (80*1000*1000)
+ regval |= EMAC_NCFGR_CLK_DIV64; /* MCK divided by 64 (MCK up to 160 MHz) */
+#elif BOARD_MCK_FREQUENCY > (40*1000*1000)
+ regval |= EMAC_NCFGR_CLK_DIV32; /* MCK divided by 32 (MCK up to 80 MHz) */
+#elif BOARD_MCK_FREQUENCY > (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) */
+#endif
+
+ sam_putreg(priv, SAM_EMAC_NCFGR, regval);
+
+ /* Check the PHY Address */
+
+ priv->phyaddr = CONFIG_SAM34_EMAC_PHYADDR;
+ ret = sam_phyfind(priv, &priv->phyaddr);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: sam_phyfind failed: %d\n", ret);
+ return ret;
+ }
+
+ if (priv->phyaddr != CONFIG_SAM34_EMAC_PHYADDR)
+ {
+ sam_phyreset(priv);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: sam_ethgpioconfig
+ *
+ * Description:
+ * Configure GPIOs for the EMAC interface.
+ *
+ * Parameters:
+ * priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline void sam_ethgpioconfig(struct sam_emac_s *priv)
+{
+ /* Configure PIO pins to support EMAC */
+ /* Configure EMAC PIO pins common to both MII and RMII */
+
+ sam_configgpio(GPIO_EMAC_TX0);
+ sam_configgpio(GPIO_EMAC_TX1);
+ sam_configgpio(GPIO_EMAC_RX0);
+ sam_configgpio(GPIO_EMAC_RX1);
+ sam_configgpio(GPIO_EMAC_TXEN);
+ sam_configgpio(GPIO_EMAC_CRSDV);
+ sam_configgpio(GPIO_EMAC_RXER);
+ sam_configgpio(GPIO_EMAC_REFCK);
+ sam_configgpio(GPIO_EMAC_MDC);
+ sam_configgpio(GPIO_EMAC_MDIO);
+}
+
+/****************************************************************************
+ * 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);
+ regval &= ~EMAC_NCR_TXEN;
+ sam_putreg(priv, SAM_EMAC_NCR, regval);
+
+ /* Configure the TX descriptors. */
+
+ priv->txhead = 0;
+ priv->txtail = 0;
+
+ for (ndx = 0; ndx < CONFIG_SAM34_EMAC_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[CONFIG_SAM34_EMAC_NTXBUFFERS - 1].status =
+ EMACTXD_STA_USED | EMACTXD_STA_WRAP;
+
+ /* Set the Transmit Buffer Queue Pointer Register */
+
+ sam_putreg(priv, SAM_EMAC_TBQB, (uintptr_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);
+ regval &= ~EMAC_NCR_RXEN;
+ sam_putreg(priv, SAM_EMAC_NCR, regval);
+
+ /* Configure the RX descriptors. */
+
+ priv->rxndx = 0;
+ for (ndx = 0; ndx < CONFIG_SAM34_EMAC_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[CONFIG_SAM34_EMAC_NRXBUFFERS - 1].addr |= EMACRXD_ADDR_WRAP;
+
+ /* Set the Receive Buffer Queue Pointer Register */
+
+ sam_putreg(priv, SAM_EMAC_RBQB, (uintptr_t)rxdesc);
+}
+
+/****************************************************************************
+ * 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)
+{
+ uint32_t regval;
+
+ /* Disable all EMAC interrupts */
+
+ sam_putreg(priv, SAM_EMAC_IDR, EMAC_INT_ALL);
+
+ /* Reset RX and TX logic */
+
+ sam_rxreset(priv);
+ sam_txreset(priv);
+
+ /* Disable RX, TX, and statistics */
+
+ regval = EMAC_NCR_TXEN | EMAC_NCR_RXEN | EMAC_NCR_WESTAT | EMAC_NCR_CLRSTAT;
+ sam_putreg(priv, SAM_EMAC_NCR, regval);
+
+ /* Disable clocking to the EMAC peripheral */
+
+ sam_emac_disableclk();
+}
+
+/****************************************************************************
+ * 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 uip_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, 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, regval);
+}
+
+/****************************************************************************
+ * 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();
+
+ /* Disable TX, RX, interrupts, etc. */
+
+ sam_putreg(priv, SAM_EMAC_NCR, 0);
+ sam_putreg(priv, SAM_EMAC_IDR, EMAC_INT_ALL);
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR);
+ regval |= EMAC_NCR_CLRSTAT;
+ sam_putreg(priv, SAM_EMAC_NCR, regval);
+
+ /* 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, 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 | EMAC_TSR_UND);
+ sam_putreg(priv, SAM_EMAC_TSR, regval);
+
+ /* Clear any pending interrupts */
+
+ (void)sam_getreg(priv, SAM_EMAC_ISR);
+
+ /* Enable/disable the copy of data into the buffers, ignore broadcasts.
+ * Don't copy FCS.
+ */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCFGR);
+ regval |= (EMAC_NCFGR_RFCS | EMAC_NCFGR_PEN);
+
+#ifdef CONFIG_NET_PROMISCUOUS
+ regval |= EMAC_NCFGR_CAF;
+#else
+ regval &= ~EMAC_NCFGR_CAF;
+#endif
+
+#ifdef CONFIG_SAM34_EMAC_NBC
+ regval |= EMAC_NCFGR_NBC;
+#else
+ regval &= ~EMAC_NCFGR_NBC;
+#endif
+
+ sam_putreg(priv, SAM_EMAC_NCFGR, regval);
+
+ /* Reset TX and RX */
+
+ sam_rxreset(priv);
+ sam_txreset(priv);
+
+ /* Enable Rx and Tx, plus the stats register. */
+
+ regval = sam_getreg(priv, SAM_EMAC_NCR);
+ regval |= (EMAC_NCR_RXEN | EMAC_NCR_TXEN | EMAC_NCR_WESTAT);
+ sam_putreg(priv, SAM_EMAC_NCR, 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_PFNX | EMAC_INT_PTZ);
+ sam_putreg(priv, SAM_EMAC_IER, regval);
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: up_netinitialize
+ *
+ * Description:
+ * Initialize the EMAC driver.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+void up_netinitialize(void)
+{
+ struct sam_emac_s *priv = &g_emac;
+ int ret;
+
+ /* Initialize the driver structure */
+
+ memset(priv, 0, sizeof(struct sam_emac_s));
+ 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
+ priv->dev.d_private = (void*)&g_emac; /* Used to recover private state from dev */
+
+ /* Create a watchdog for timing polling for and timing of transmisstions */
+
+ priv->txpoll = wd_create();
+ if (!priv->txpoll)
+ {
+ nlldbg("ERROR: Failed to create periodic poll timer\n");
+ goto errout;
+ }
+
+ priv->txtimeout = wd_create(); /* Create TX timeout timer */
+ if (!priv->txpoll)
+ {
+ nlldbg("ERROR: Failed to create periodic poll timer\n");
+ goto errout_with_txpoll;
+ }
+
+ /* Configure PIO pins to support EMAC */
+
+ sam_ethgpioconfig(priv);
+
+ /* Allocate buffers */
+
+ ret = sam_buffer_initialize(priv);
+ if (ret < 0)
+ {
+ nlldbg("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(SAM_IRQ_EMAC, sam_emac_interrupt);
+ if (ret < 0)
+ {
+ nlldbg("ERROR: Failed to attach the handler to the IRQ%d\n", SAM_IRQ_EMAC);
+ goto errout_with_buffers;
+ }
+
+ /* Enable clocking to the EMAC peripheral (just for sam_ifdown()) */
+
+ sam_emac_enableclk();
+
+ /* Put the interface in the down state (disabling clocking again). */
+
+ ret = sam_ifdown(&priv->dev);
+ if (ret < 0)
+ {
+ nlldbg("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);
+ if (ret >= 0)
+ {
+ return ret;
+ }
+
+ nlldbg("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_SAM34_EMAC */
diff --git a/nuttx/arch/arm/src/sam34/sam_emac.h b/nuttx/arch/arm/src/sam34/sam_emac.h
new file mode 100644
index 000000000..1c1e69aa4
--- /dev/null
+++ b/nuttx/arch/arm/src/sam34/sam_emac.h
@@ -0,0 +1,120 @@
+/************************************************************************************
+ * arch/arm/src/sam34/sam_ethernet.h
+ *
+ * Copyright (C) 2014 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_SAM34_SAM_ETHERNET_H
+#define __ARCH_ARM_SRC_SAM34_SAM_ETHERNET_H
+
+/************************************************************************************
+ * Included Files
+ ************************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+#include "chip/sam_emac.h"
+
+#ifdef CONFIG_SAM34_EMAC
+
+/************************************************************************************
+ * Pre-processor Definitions
+ ************************************************************************************/
+
+/************************************************************************************
+ * Public Functions
+ ************************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Function: up_netinitialize
+ *
+ * Description:
+ * Initialize the EMAC driver. Also prototyped in up_internal.h.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ * Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+void up_netinitialize(void);
+
+/************************************************************************************
+ * Function: 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_SAM34_PHYINIT is defined in the configuration then the board specific
+ * logic must provide sam_phyinitialize(); The SAM34 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_SAM34_PHYINIT
+int sam_phy_boardinitialize(int intf);
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* CONFIG_SAM34_EMAC */
+#endif /* __ARCH_ARM_SRC_SAM34_SAM_ETHERNET_H */
+
diff --git a/nuttx/configs/sam4e-ek/README.txt b/nuttx/configs/sam4e-ek/README.txt
index 62b6b1981..922ae711e 100644
--- a/nuttx/configs/sam4e-ek/README.txt
+++ b/nuttx/configs/sam4e-ek/README.txt
@@ -1,12 +1,12 @@
README
-^^^^^^
+======
This README discusses issues unique to NuttX configurations for the Atmel
SAM4E-EK development. This board features the SAM4E16 MCU running at 96
or 120MHz.
Contents
-^^^^^^^^
+========
- Development Environment
- GNU Toolchain Options
@@ -19,11 +19,12 @@ Contents
- Writing to FLASH using SAM-BA
- LEDs
- Serial Console
+ - Networking Support
- SAM4E-EK-specific Configuration Options
- Configurations
Development Environment
-^^^^^^^^^^^^^^^^^^^^^^^
+=======================
Either Linux or Cygwin on Windows can be used for the development environment.
The source has been built only using the GNU toolchain (see below). Other
@@ -31,7 +32,7 @@ Development Environment
environment.
GNU Toolchain Options
-^^^^^^^^^^^^^^^^^^^^^
+=====================
The NuttX make system can be configured to support the various different
toolchain options. All testing has been conducted using the NuttX buildroot
@@ -86,7 +87,7 @@ GNU Toolchain Options
MKDEP = $(TOPDIR)/tools/mknulldeps.sh
IDEs
-^^^^
+====
NuttX is built using command-line make. It can be used with an IDE, but some
effort will be required to create the project (There is a simple RIDE project
@@ -119,7 +120,7 @@ IDEs
startup object needed by RIDE.
NuttX EABI "buildroot" Toolchain
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+================================
A GNU GCC-based toolchain is assumed. The files */setenv.sh should
be modified to point to the correct path to the Cortex-M3 GCC toolchain (if
@@ -162,7 +163,7 @@ NuttX EABI "buildroot" Toolchain
See instructions below.
NuttX OABI "buildroot" Toolchain
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+================================
The older, OABI buildroot toolchain is also available. To use the OABI
toolchain:
@@ -181,7 +182,7 @@ NuttX OABI "buildroot" Toolchain
-NXFLATLDFLAGS2 = $(NXFLATLDFLAGS1) -T$(TOPDIR)/binfmt/libnxflat/gnu-nxflat-pcrel.ld -no-check-sections
NXFLAT Toolchain
-^^^^^^^^^^^^^^^^
+================
If you are *not* using the NuttX buildroot toolchain and you want to use
the NXFLAT tools, then you will still have to build a portion of the buildroot
@@ -214,7 +215,7 @@ NXFLAT Toolchain
the path to the newly builtNXFLAT binaries.
Atmel Studio 6.1
-^^^^^^^^^^^^^^^^
+================
You can use Atmel Studio 6.1 to load and debug code.
@@ -247,7 +248,7 @@ Atmel Studio 6.1
not readable. A little more needs to be done to wring out this procedure.
Loading Code into SRAM with J-Link
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+==================================
Loading code with the Segger tools and GDB
------------------------------------------
@@ -277,7 +278,7 @@ Loading Code into SRAM with J-Link
debugging after writing the program to FLASH using SAM-BA.
Writing to FLASH using SAM-BA
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+=============================
Assumed starting configuration:
@@ -296,7 +297,7 @@ Writing to FLASH using SAM-BA
STATUS: Works great!
LEDs
-^^^^
+====
The SAM4E-EK board has three, user-controllable LEDs labelled D2 (blue),
D3 (amber), and D4 (green) on the board. Usage of these LEDs is defined
@@ -321,7 +322,7 @@ LEDs
*** D4 may also flicker normally if signals are processed.
Serial Console
-^^^^^^^^^^^^^^
+==============
By default, all of these configurations use UART0 for the NuttX serial
console. UART0 corresponds to the DB-9 connector J17 labelled "DBGU".
@@ -342,8 +343,153 @@ Serial Console
By default serial console is configured for 115000, 8-bit, 1 stop bit, and
no parity.
+Networking
+==========
+
+ Networking support via the can be added to NSH by selecting the following
+ configuration options.
+
+ Selecting the EMAC peripheral
+ -----------------------------
+
+ System Type -> SAM34 Peripheral Support
+ CONFIG_SAM34_EMAC=y : Enable the EMAC peripheral
+
+ System Type -> EMAC device driver options
+ CONFIG_SAM34_EMAC_NRXBUFFERS=16 : Set aside some RS and TX buffers
+ CONFIG_SAM34_EMAC_NTXBUFFERS=4
+ CONFIG_SAM34_EMAC_PHYADDR=1 : KSZ8051 PHY is at address 1
+ CONFIG_SAM34_EMAC_AUTONEG=y : Use autonegotiation
+ CONFIG_SAM34_EMAC_MII=y : Only the MII interface is supported
+ CONFIG_SAM34_EMAC_PHYSR=30 : Address of PHY status register on KSZ8051
+ CONFIG_SAM34_EMAC_PHYSR_ALTCONFIG=y : Needed for KSZ8051
+ CONFIG_SAM34_EMAC_PHYSR_ALTMODE=0x7 : " " " " " "
+ CONFIG_SAM34_EMAC_PHYSR_10HD=0x1 : " " " " " "
+ CONFIG_SAM34_EMAC_PHYSR_100HD=0x2 : " " " " " "
+ CONFIG_SAM34_EMAC_PHYSR_10FD=0x5 : " " " " " "
+ CONFIG_SAM34_EMAC_PHYSR_100FD=0x6 : " " " " " "
+
+ PHY selection. Later in the configuration steps, you will need to select
+ the KSZ8051 PHY for EMAC (See below)
+
+ Networking Support
+ CONFIG_NET=y : Enable Neworking
+ CONFIG_NET_SOCKOPTS=y : Enable socket operations
+ CONFIG_NET_BUFSIZE=562 : Maximum packet size (MTD) 1518 is more standard
+ CONFIG_NET_RECEIVE_WINDOW=536 : Should be the same as CONFIG_NET_BUFSIZE
+ CONFIG_NET_TCP=y : Enable TCP/IP networking
+ CONFIG_NET_TCPBACKLOG=y : Support TCP/IP backlog
+ CONFIG_NET_TCP_READAHEAD_BUFSIZE=536 Read-ahead buffer size
+ CONFIG_NET_UDP=y : Enable UDP networking
+ CONFIG_NET_BROADCAST=y : Needed for DNS name resolution
+ 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_KSZ8051=y : Select the KSZ8051 PHY (for EMAC)
+
+ Application Configuration -> Network Utilities
+ CONFIG_NETUTILS_RESOLV=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_UIPLIB=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 SAM4E-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 SAM4E-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-6.31
+ 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.
+
+
SAM4E-EK-specific Configuration Options
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+=======================================
CONFIG_ARCH - Identifies the arch/ subdirectory. This should
be set to:
@@ -504,7 +650,7 @@ SAM4E-EK-specific Configuration Options
support a 320x240 "Landscape" orientation.
Configurations
-^^^^^^^^^^^^^^
+==============
Information Common to All Configurations
----------------------------------------
diff --git a/nuttx/net/Kconfig b/nuttx/net/Kconfig
index 2481bb6b3..d1ba21d31 100644
--- a/nuttx/net/Kconfig
+++ b/nuttx/net/Kconfig
@@ -133,7 +133,7 @@ config NET_MAX_LISTENPORTS
Maximum number of listening TCP/IP ports (all tasks). Default: 20
config NET_TCP_READAHEAD
- bool "Enabled TCP/IP read-ahead buffering"
+ bool "Enable TCP/IP read-ahead buffering"
default y
---help---
Read-ahead buffers allows buffering of TCP/IP packets when there is no
@@ -179,7 +179,7 @@ config NET_NTCP_READAHEAD_BUFFERS
endif # NET_TCP_READAHEAD
config NET_TCP_WRITE_BUFFERS
- bool "Enabled TCP/IP write buffering"
+ bool "Enable TCP/IP write buffering"
default n
---help---
Write buffers allows buffering of ongoing TCP/IP packets, providing