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