diff --git a/boards/xtensa/esp32s3/common/src/Make.defs b/boards/xtensa/esp32s3/common/src/Make.defs index 47196bf4624..dbb2b82ca53 100644 --- a/boards/xtensa/esp32s3/common/src/Make.defs +++ b/boards/xtensa/esp32s3/common/src/Make.defs @@ -56,6 +56,10 @@ ifeq ($(CONFIG_ESP32S3_OTG),y) CSRCS += esp32s3_board_usb.c endif +ifeq ($(CONFIG_NET_LAN9250),y) + CSRCS += esp32s3_lan9250.c +endif + DEPPATH += --dep-path src VPATH += :src CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)src diff --git a/boards/xtensa/esp32s3/common/src/esp32s3_lan9250.c b/boards/xtensa/esp32s3/common/src/esp32s3_lan9250.c new file mode 100644 index 00000000000..8981a24b98c --- /dev/null +++ b/boards/xtensa/esp32s3/common/src/esp32s3_lan9250.c @@ -0,0 +1,245 @@ +/**************************************************************************** + * boards/xtensa/esp32s3/common/src/esp32s3_lan9250.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "xtensa.h" +#include "esp32s3_gpio.h" +#ifdef CONFIG_LAN9250_SPI +#include "esp32s3_spi.h" +#else +#include "esp32s3_qspi.h" +#endif +#include "hardware/esp32s3_efuse.h" +#include "hardware/esp32s3_gpio_sigmap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Function Protototypes + ****************************************************************************/ + +static int lan9250_attach(const struct lan9250_lower_s *lower, + xcpt_t handler, void *arg); +static void lan9250_enable(const struct lan9250_lower_s *lower); +static void lan9250_disable(const struct lan9250_lower_s *lower); +static void lan9250_getmac(const struct lan9250_lower_s *lower, + uint8_t *mac); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct lan9250_lower_s g_lan9250_lower = +{ + .attach = lan9250_attach, + .enable = lan9250_enable, + .disable = lan9250_disable, + .getmac = lan9250_getmac +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lan9250_attach + * + * Description: + * Attach LAN9250 interrupt. + * + * Input Parameters: + * lower - A reference to the LAN9250 low-level object data. + * handler - Interrupt handle + * arg - Interrupt private argument + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int lan9250_attach(const struct lan9250_lower_s *lower, + xcpt_t handler, void *arg) +{ + int ret; + int irq = ESP32S3_PIN2IRQ(LAN9250_IRQ); + + ret = irq_attach(irq, handler, arg); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: irq_attach() failed: %d\n", ret); + return ret; + } + + ninfo("Attach the interrupt\n"); + + return 0; +} + +/**************************************************************************** + * Name: lan9250_enable + * + * Description: + * Enable LAN9250 interrupt. + * + * Input Parameters: + * lower - A reference to the LAN9250 low-level object data. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_enable(const struct lan9250_lower_s *lower) +{ + int irq = ESP32S3_PIN2IRQ(LAN9250_IRQ); + + /* Configure the interrupt for rising and falling edges */ + + esp32s3_gpioirqenable(irq, GPIO_INTR_LOW_LEVEL); + ninfo("Enable the interrupt\n"); +} + +/**************************************************************************** + * Name: lan9250_disable + * + * Description: + * Disable LAN9250 interrupt. + * + * Input Parameters: + * lower - A reference to the LAN9250 low-level object data. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_disable(const struct lan9250_lower_s *lower) +{ + int irq = ESP32S3_PIN2IRQ(LAN9250_IRQ); + + ninfo("Disable the interrupt\n"); + esp32s3_gpioirqdisable(irq); +} + +/**************************************************************************** + * Name: lan9250_getmac + * + * Description: + * Retrieves the ESP32-S3 MAC address to be set in the LAN9250 driver. + * + * Input Parameters: + * lower - A reference to the LAN9250 low-level object data. + * mac - A pointer to a buffer where the MAC address will be stored. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_getmac(const struct lan9250_lower_s *lower, uint8_t *mac) +{ + uint32_t regval[2]; + uint8_t *data = (uint8_t *)regval; + + regval[0] = getreg32(EFUSE_RD_MAC_SPI_SYS_0_REG); + regval[1] = getreg32(EFUSE_RD_MAC_SPI_SYS_1_REG); + + for (int i = 0; i < 6; i++) + { + mac[i] = data[i]; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_lan9250_initialize + * + * Description: + * This function is called by platform-specific setup logic to initialize + * the LAN9250 device. This function will register the driver + * as a network device. + * + * Input Parameters: + * port - The SPI port used for the device + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int esp32s3_lan9250_initialize(int port) +{ + int ret; +#ifdef CONFIG_LAN9250_SPI + struct spi_dev_s *dev; +#else + struct qspi_dev_s *dev; +#endif + + esp32s3_configgpio(LAN9250_IRQ, INPUT_FUNCTION_2 | PULLUP); + esp32s3_configgpio(LAN9250_RST, OUTPUT_FUNCTION_2 | PULLUP); + +#ifdef CONFIG_LAN9250_SPI + dev = esp32s3_spibus_initialize(port); + if (!dev) + { + nerr("ERROR: Failed to initialize SPI port %d\n", port); + return -ENODEV; + } +#else + dev = esp32s3_qspibus_initialize(port); + if (!dev) + { + nerr("ERROR: Failed to initialize QSPI port %d\n", port); + return -ENODEV; + } +#endif + + ret = lan9250_initialize(dev, &g_lan9250_lower); + if (ret != 0) + { + nerr("ERROR: Failed to initialize LAN9250 ret=%d\n", ret); + return ret; + } + + return 0; +} + diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/configs/eth_lan9250/defconfig b/boards/xtensa/esp32s3/esp32s3-devkit/configs/eth_lan9250/defconfig new file mode 100644 index 00000000000..798c4c7d4e6 --- /dev/null +++ b/boards/xtensa/esp32s3/esp32s3-devkit/configs/eth_lan9250/defconfig @@ -0,0 +1,75 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_LEDS is not set +# CONFIG_ESP32S3_RTC is not set +# CONFIG_NDEBUG is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +# CONFIG_SPI_EXCHANGE is not set +CONFIG_ALLOW_BSD_COMPONENTS=y +CONFIG_ARCH="xtensa" +CONFIG_ARCH_BOARD="esp32s3-devkit" +CONFIG_ARCH_BOARD_COMMON=y +CONFIG_ARCH_BOARD_ESP32S3_DEVKIT=y +CONFIG_ARCH_CHIP="esp32s3" +CONFIG_ARCH_CHIP_ESP32S3=y +CONFIG_ARCH_CHIP_ESP32S3WROOM1=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARCH_XTENSA=y +CONFIG_BOARD_LOOPSPERMSEC=16717 +CONFIG_BUILTIN=y +CONFIG_DEFAULT_TASK_STACKSIZE=4096 +CONFIG_ESP32S3_GPIO_IRQ=y +CONFIG_ESP32S3_SPI2=y +CONFIG_ESP32S3_SPI_SWCS=y +CONFIG_ESP32S3_UART0=y +CONFIG_FS_PROCFS=y +CONFIG_IDLETHREAD_STACKSIZE=3072 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INIT_STACKSIZE=8192 +CONFIG_INTELHEX_BINARY=y +CONFIG_IOB_BUFSIZE=1514 +CONFIG_IOB_NBUFFERS=5 +CONFIG_LAN9250_FREQUENCY=40000000 +CONFIG_LAN9250_SPI_EXCLUSIVE=y +CONFIG_NAME_MAX=48 +CONFIG_NET=y +CONFIG_NETDB_DNSCLIENT=y +CONFIG_NETDEV_LATEINIT=y +CONFIG_NETUTILS_IPERF=y +CONFIG_NET_ARP_SEND=y +CONFIG_NET_ETH_PKTSIZE=1518 +CONFIG_NET_ICMP=y +CONFIG_NET_ICMP_SOCKET=y +CONFIG_NET_LAN9250=y +CONFIG_NET_TCP=y +CONFIG_NET_UDP=y +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_LINELEN=64 +CONFIG_NSH_READLINE=y +CONFIG_POSIX_SPAWN_DEFAULT_STACKSIZE=2048 +CONFIG_PREALLOC_TIMERS=4 +CONFIG_PTHREAD_MUTEX_TYPES=y +CONFIG_RAM_SIZE=114688 +CONFIG_RAM_START=0x20000000 +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_LPWORK=y +CONFIG_SCHED_WAITPID=y +CONFIG_SIG_DEFAULT=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_SYSLOG_BUFFER=y +CONFIG_SYSTEM_DHCPC_RENEW=y +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_PING=y +CONFIG_UART0_BAUD=2000000 +CONFIG_UART0_SERIAL_CONSOLE=y diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/include/board.h b/boards/xtensa/esp32s3/esp32s3-devkit/include/board.h index 54e89c98f5a..9c2690cece2 100644 --- a/boards/xtensa/esp32s3/esp32s3-devkit/include/board.h +++ b/boards/xtensa/esp32s3/esp32s3-devkit/include/board.h @@ -49,4 +49,19 @@ #define BOARD_NGPIOIN 1 /* Amount of GPIO Input without Interruption */ #define BOARD_NGPIOINT 1 /* Amount of GPIO Input w/ Interruption pins */ +/* LAN9250 SPI and GPIO pins configuration */ + +#ifdef CONFIG_NET_LAN9250 + +# define LAN9250_SPI 2 + +/* LAN9250 IRQ pin */ + +# define LAN9250_IRQ 38 + +/* LAN9250 reset pin */ + +# define LAN9250_RST 17 +#endif + #endif /* __BOARDS_XTENSA_ESP32S3_ESP32S3_DEVKIT_INCLUDE_BOARD_H */ diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3-devkit.h b/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3-devkit.h index 62b9acfc5fe..215a69468ab 100644 --- a/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3-devkit.h +++ b/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3-devkit.h @@ -210,5 +210,26 @@ int esp32s3_pwm_setup(void); int esp32s3_twai_setup(void); #endif +/**************************************************************************** + * Name: esp32s3_lan9250_initialize + * + * Description: + * This function is called by platform-specific setup logic to initialize + * the LAN9250 device. This function will register the driver + * as a network device. + * + * Input Parameters: + * port - The SPI port used for the device + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_LAN9250 +int esp32s3_lan9250_initialize(int port); +#endif + #endif /* __ASSEMBLY__ */ #endif /* __BOARDS_XTENSA_ESP32S3_ESP32S3_DEVKIT_SRC_ESP32S3_DEVKIT_H */ diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c b/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c index 23e8ed687f5..51e8c8442ce 100644 --- a/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c +++ b/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c @@ -38,6 +38,7 @@ #include #include #include +#include #ifdef CONFIG_ESP32S3_TIMER # include "esp32s3_board_tim.h" @@ -388,6 +389,14 @@ int esp32s3_bringup(void) } #endif +#ifdef CONFIG_NET_LAN9250 + ret = esp32s3_lan9250_initialize(LAN9250_SPI); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to initialize SPI ethernet LAN9250.\n"); + } +#endif + /* If we got here then perhaps not all initialization was successful, but * at least enough succeeded to bring-up NSH with perhaps reduced * capabilities. diff --git a/drivers/net/CMakeLists.txt b/drivers/net/CMakeLists.txt index f3992519493..9d5492d249f 100644 --- a/drivers/net/CMakeLists.txt +++ b/drivers/net/CMakeLists.txt @@ -75,6 +75,10 @@ if(CONFIG_NET) list(APPEND SRCS w5500.c) endif() + if(CONFIG_NET_LAN9250) + list(APPEND SRCS lan9250.c) + endif() + if(CONFIG_ARCH_PHY_INTERRUPT) list(APPEND SRCS phy_notify.c) endif() diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index d1f87797abb..c34bc9263af 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -428,6 +428,69 @@ config NET_W5500_NINTERFACES endif # W5500 +menuconfig NET_LAN9250 + bool "Microchip LAN9250 support" + default n + select SPI + select ARCH_HAVE_NETDEV_STATISTICS + ---help--- + Enable LAN9250 SPI Ethernet driver. + +if NET_LAN9250 + +choice LAN9250_INTERFACE + prompt "Select interface" + default LAN9250_SPI + ---help--- + Select LAN9250 interface: SPI(4-line) or SQI(6-line). + +config LAN9250_SPI + bool "SPI" + +config LAN9250_SQI + bool "SQI" +endchoice + +config LAN9250_SPI_EXCLUSIVE + bool "SPI Exclusive" + default n + ---help--- + The SPI is exclusive for LAN9250. + +config LAN9250_SPIMODE + int "SPI mode" + default 0 + ---help--- + Controls the SPI mode. + +config LAN9250_FREQUENCY + int "SPI frequency" + default 10000000 + ---help--- + Define to use a different bus frequency. + +config LAN9250_HALFDUPPLEX + bool "Enable half duplex" + default n + ---help--- + Default is full duplex. + +config LAN9250_DUMPPACKET + bool "Dump Packets" + default n + ---help--- + If selected, the LAN9250 driver will dump the contents of each + packet to the console. + +config LAN9250_REGDEBUG + bool "Register-Level Debug" + default n + depends on DEBUG_FEATURES && DEBUG_NET + ---help--- + Enable very low-level register access debug. + +endif # NET_LAN9250 + if ARCH_HAVE_PHY comment "External Ethernet PHY Device Support" diff --git a/drivers/net/Make.defs b/drivers/net/Make.defs index 070df18e55e..c1f65a214dd 100644 --- a/drivers/net/Make.defs +++ b/drivers/net/Make.defs @@ -76,6 +76,10 @@ ifeq ($(CONFIG_NET_W5500),y) CSRCS += w5500.c endif +ifeq ($(CONFIG_NET_LAN9250),y) + CSRCS += lan9250.c +endif + ifeq ($(CONFIG_ARCH_PHY_INTERRUPT),y) CSRCS += phy_notify.c endif diff --git a/drivers/net/lan9250.c b/drivers/net/lan9250.c new file mode 100644 index 00000000000..e2bba339541 --- /dev/null +++ b/drivers/net/lan9250.c @@ -0,0 +1,2452 @@ +/**************************************************************************** + * drivers/net/lan9250.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_NET_PKT +# include +#endif + +#ifdef CONFIG_LAN9250_SPI +# include +#else +# include +#endif + +#include "lan9250.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* LAN9250 SPI clock */ + +#define LAN9250_FREQUENCY CONFIG_LAN9250_FREQUENCY + +/* LAN9250 read and write command */ + +#ifdef CONFIG_LAN9250_SPI +# if LAN9250_FREQUENCY > (30 * 1000 * 1000) +# define LAN9250_SPI_READ LAN9250_SPI_CMD_FREAD +# else +# define LAN9250_SPI_READ LAN9250_SPI_CMD_READ +# endif + +# define LAN9250_SPI_WRITE LAN9250_SPI_CMD_WRITE +#else +# define LAN9250_SPI_READ LAN9250_QSPI_CMD_READ +# define LAN9250_SPI_WRITE LAN9250_QSPI_CMD_WRITE + +# define LAN9250_SPI_ENABLE_SQI LAN9250_SPI_CMD_ENABLE_SQI +#endif + +/** + * LAN9250 interrupt trigger source: + * + * - PHY for checking link up or down + * - TX data FIFO available + * - RX data FIFO reaches programmed level + */ + +#define LAN9250_INT_SOURCE (IER_PHY | IER_TDFA | IER_RSFL) + +/* The low-priority work queue is preferred. If it is not enabled, LPWORK + * will be the same as HPWORK. + * + * NOTE: However, the network should NEVER run on the high priority work + * queue! That queue is intended only to service short back end interrupt + * processing that never suspends. Suspending the high priority work queue + * may bring the system to its knees! + */ + +#define LAN9250_WORK LPWORK + +/* Timing *******************************************************************/ + +/* Reset timeout in second */ + +#define LAN9250_RESET_TIMEOUT (5) + +/* TX timeout = 1 minute */ + +#define LAN9250_TX_TIMEOUT (60 * CLK_TCK) + +/* Poll timeout */ + +#define LAN9250_POLL_TIMEOUT MSEC2TICK(50) + +/* Read/Write MAC register timeout in second */ + +#define LAN9250_MAC_TIMEOUT 2 + +/* Read/Write PHY register timeout in second */ + +#define LAN9250_PHY_TIMEOUT 2 + +/* Read/Write PHY register timeout in millisecond */ + +#define LAN9250_SQI_TIMEOUT 20 + +/* Packet Memory ************************************************************/ + +/* Misc. Helper Macros ******************************************************/ + +#define LAN9250_ALIGN(v) (((v) + 3) & (~3)) + +/* Packet buffer size with 4-CRC */ + +#define LAN9250_PKTBUF_SIZE LAN9250_ALIGN(MAX_NETDEV_PKTSIZE + \ + CONFIG_NET_GUARDSIZE + \ + 4) + +/* This is a helper pointer for accessing the contents of Ethernet header */ + +#define BUF ((struct eth_hdr_s *)priv->dev.d_buf) + +/* Debug ********************************************************************/ + +#ifdef CONFIG_LAN9250_REGDEBUG +# define lan9250_setreg_dump(a, v) \ + syslog(LOG_DEBUG, "LAN9250 REG: 0x%04x<-0x%08x\n", a, v) +# define lan9250_getreg_dump(a, v) \ + syslog(LOG_DEBUG, "LAN9250 REG: 0x%04x->0x%08x\n", a, v) +# define lan9250_setmacreg_dump(a, v) \ + syslog(LOG_DEBUG, "LAN9250 MAC: 0x%02x<-0x%08x\n", a, v) +# define lan9250_getmacreg_dump(a, v) \ + syslog(LOG_DEBUG, "LAN9250 MAC: 0x%02x->0x%08x\n", a, v) +# define lan9250_setphyreg_dump(a, v) \ + syslog(LOG_DEBUG, "LAN9250 PHY: 0x%02x<-0x%08x\n", a, v) +# define lan9250_getphyreg_dump(a, v) \ + syslog(LOG_DEBUG, "LAN9250 PHY: 0x%02x->0x%08x\n", a, v) +# define lan9250_buffer_dump(c, b, s) \ + syslog(LOG_DEBUG, "LAN9250 BUF: cmd: %04x buffer: %p length: %d\n", c, b, s) +#else +# define lan9250_setreg_dump(a, v) +# define lan9250_getreg_dump(a, v) +# define lan9250_setmacreg_dump(a, v) +# define lan9250_getmacreg_dump(a, v) +# define lan9250_setphyreg_dump(a, v) +# define lan9250_getphyreg_dump(a, v) +# define lan9250_buffer_dump(c, b, s) +#endif + +/* CONFIG_LAN9250_DUMPPACKET will dump the contents of each packet. */ + +#ifdef CONFIG_LAN9250_DUMPPACKET +# define lan9250_dump_buf(m, a, n) lib_dumpbuffer(m, a, n) +#else +# define lan9250_dump_buf(m, a, n) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* The lan9250_driver_s encapsulates all state information for a single + * hardware interface + */ + +struct lan9250_driver_s +{ + /* Low-level MCU-specific support */ + + const struct lan9250_lower_s *lower; + + /* This is the contained (Q)SPI driver instance */ + +#ifdef CONFIG_LAN9250_SPI + struct spi_dev_s *spi; +#else + bool sqi_mode; + struct qspi_dev_s *qspi; +#endif + + bool tx_available; /* TX is available */ + + /* Read/Write buffer for SPI transmission */ + + uint32_t pktbuf[LAN9250_PKTBUF_SIZE / 4]; + + struct wdog_s txtout_timer; /* TX timeout timer */ + struct work_s txtout_work; /* Tx timeout work */ + struct work_s txpoll_work; /* TX poll work */ + struct work_s irq_work; /* Interrupt work */ + + /* This holds the information visible to the NuttX network */ + + struct net_driver_s dev; /* Interface understood by the network */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Driver status structure */ + +static struct lan9250_driver_s g_lan9250; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Low-level SPI helpers */ + +static inline void lan9250_config_spi(FAR struct lan9250_driver_s *priv); +static void lan9250_lock_spi(FAR struct lan9250_driver_s *priv); +static inline void lan9250_unlock_spi(FAR struct lan9250_driver_s *priv); + +/* SPI control register access */ + +static uint32_t lan9250_get_reg(FAR struct lan9250_driver_s *priv, + uint16_t address); +static void lan9250_set_reg(FAR struct lan9250_driver_s *priv, + uint16_t address, uint32_t data); +static void lan9250_wait_ready(FAR struct lan9250_driver_s *priv, + uint16_t address, uint32_t mask, + uint32_t expected, uint32_t timeout); + +/* MAC register access */ + +static uint32_t lan9250_get_macreg(FAR struct lan9250_driver_s *priv, + uint8_t address); +static void lan9250_set_macreg(FAR struct lan9250_driver_s *priv, + uint8_t address, uint32_t data); +static void lan9250_wait_mac_ready(FAR struct lan9250_driver_s *priv, + uint8_t address, uint32_t mask, + uint32_t expected, uint32_t timeout); + +/* PHY register access */ + +static uint16_t lan9250_get_phyreg(FAR struct lan9250_driver_s *priv, + uint8_t phyaddr); +static void lan9250_set_phyreg(FAR struct lan9250_driver_s *priv, + uint8_t phyaddr, uint16_t phydata); + +/* SPI buffer transfers */ + +static void lan9250_recv_buffer(FAR struct lan9250_driver_s *priv, + FAR uint8_t *buffer, size_t buflen); +static inline void lan9250_send_buffer(FAR struct lan9250_driver_s *priv, + FAR const uint8_t *buffer, + size_t buflen); + +/* Misc handling */ + +#ifdef CONFIG_LAN9250_SQI +static void lan9250_enable_sqi(FAR struct lan9250_driver_s *priv); +#endif +static inline void lan9250_sw_reset(FAR struct lan9250_driver_s *priv); +static void lan9250_set_txavailabe(FAR struct lan9250_driver_s *priv, + bool enable); +static int lan9250_reset(FAR struct lan9250_driver_s *priv); +static void lan9250_set_macaddr(FAR struct lan9250_driver_s *priv); + +/* Common TX logic */ + +static int lan9250_transmit(FAR struct lan9250_driver_s *priv); +static int lan9250_txpoll(FAR struct net_driver_s *dev); + +/* Interrupt handling */ + +static void lan9250_netdev_rx(FAR struct lan9250_driver_s *priv); +static void lan9250_phy_isr(FAR struct lan9250_driver_s *priv); +static void lan9250_txavailable_isr(FAR struct lan9250_driver_s *priv); +static void lan9250_rxdone_isr(FAR struct lan9250_driver_s *priv); +static int lan9250_interrupt(int irq, FAR void *context, FAR void *arg); + +/* Watchdog timer expirations */ + +static void lan9250_txtout_timercb(wdparm_t arg); + +/* Driver worker */ + +static void lan9250_txavail_work(FAR void *arg); +static void lan9250_txtout_worker(FAR void *arg); +static void lan9250_int_worker(FAR void *arg); + +/* NuttX callback functions */ + +static int lan9250_ifup(FAR struct net_driver_s *dev); +static int lan9250_ifdown(FAR struct net_driver_s *dev); +static int lan9250_txavail(FAR struct net_driver_s *dev); +#ifdef CONFIG_NET_MCASTGROUP +static int lan9250_addmac(FAR struct net_driver_s *dev, + FAR const uint8_t *mac); +static int lan9250_rmmac(FAR struct net_driver_s *dev, + FAR const uint8_t *mac); +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lan9250_config_spi + * + * Description: + * Configure the SPI for use with the LAN9250 + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void lan9250_config_spi(FAR struct lan9250_driver_s *priv) +{ +#ifdef CONFIG_LAN9250_SPI + FAR struct spi_dev_s *spi = priv->spi; + + /* Configure SPI for the LAN9250. */ + + SPI_SETMODE(spi, CONFIG_LAN9250_SPIMODE); + SPI_SETBITS(spi, 8); + SPI_HWFEATURES(spi, 0); + SPI_SETFREQUENCY(spi, LAN9250_FREQUENCY); +#else + FAR struct qspi_dev_s *qspi = priv->qspi; + + /* Configure QSPI for the LAN9250. */ + + QSPI_SETMODE(qspi, CONFIG_LAN9250_SPIMODE); + QSPI_SETBITS(qspi, 8); + QSPI_HWFEATURES(qspi, 0); + QSPI_SETFREQUENCY(qspi, LAN9250_FREQUENCY); +#endif +} + +/**************************************************************************** + * Name: lan9250_lock_spi + * + * Description: + * Select the SPI, locking it and re-configuring it if necessary. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_lock_spi(FAR struct lan9250_driver_s *priv) +{ +#ifdef CONFIG_LAN9250_SPI + + /* Lock the SPI bus in case there are multiple devices or tasks competing + * for the SPI bus. + */ + + SPI_LOCK(priv->spi, true); +#else + + /* Lock the QSPI bus in case there are multiple devices or tasks competing + * for the QSPI bus. + */ + + QSPI_LOCK(priv->qspi, true); +#endif + + /* Now make sure that the (Q)SPI bus is configured for the LAN9250 (it + * might have gotten configured for a different device while unlocked) + */ + +#ifndef CONFIG_LAN9250_SPI_EXCLUSIVE + lan9250_config_spi(priv); +#endif +} + +/**************************************************************************** + * Name: lan9250_unlock_spi + * + * Description: + * De-select the SPI + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void lan9250_unlock_spi(FAR struct lan9250_driver_s *priv) +{ +#ifdef CONFIG_LAN9250_SPI + + /* Relinquish the lock on the bus. */ + + SPI_LOCK(priv->spi, false); +#else + + /* Relinquish the lock on the bus. */ + + QSPI_LOCK(priv->qspi, false); +#endif +} + +/**************************************************************************** + * Name: lan9250_get_reg + * + * Description: + * Read a global register value. + * + * Input Parameters: + * priv - Reference to the driver state structure + * address - Register address + * + * Returned Value: + * The value read from the register. + * + ****************************************************************************/ + +static uint32_t lan9250_get_reg(FAR struct lan9250_driver_s *priv, + uint16_t address) +{ +#ifdef CONFIG_LAN9250_SPI +# if LAN9250_SPI_READ == LAN9250_SPI_CMD_FREAD + uint8_t cmd_buffer[4]; +# else + uint8_t cmd_buffer[3]; +# endif + uint32_t regval; + + cmd_buffer[0] = LAN9250_SPI_READ; + cmd_buffer[1] = (uint8_t)(address >> 8); + cmd_buffer[2] = (uint8_t)(address >> 0); +# if LAN9250_SPI_READ == LAN9250_SPI_CMD_FREAD + cmd_buffer[3] = 0xff; /* SPI read dummy */ +# endif + + /* Select LAN9250 chip */ + + SPI_SELECT(priv->spi, SPIDEV_ETHERNET(0), true); + + /* Send the read command and register address */ + + SPI_SNDBLOCK(priv->spi, cmd_buffer, sizeof(cmd_buffer)); + + /* Receive register value, total 4 bytes */ + + SPI_RECVBLOCK(priv->spi, ®val, 4); + + /* De-select LAN9250 chip */ + + SPI_SELECT(priv->spi, SPIDEV_ETHERNET(0), false); +#else + struct qspi_cmdinfo_s cmdinfo; + uint32_t regval; + uint32_t buffer[2]; + int ret; + + cmdinfo.cmd = LAN9250_SPI_READ; + cmdinfo.addr = address; + cmdinfo.addrlen = sizeof(address); + cmdinfo.buffer = buffer; + cmdinfo.buflen = sizeof(buffer); /* 4 dummy bytes */ + cmdinfo.flags = QSPICMD_ADDRESS | QSPICMD_READDATA; + if (priv->sqi_mode) + { + cmdinfo.flags |= QSPICMD_IQUAD; + } + + ret = QSPI_COMMAND(priv->qspi, &cmdinfo); + DEBUGASSERT(ret == 0); + + regval = buffer[1]; +#endif + + lan9250_getreg_dump(address, regval); + return regval; +} + +/**************************************************************************** + * Name: lan9250_set_reg + * + * Description: + * Write to a global register. + * + * Input Parameters: + * priv - Reference to the driver state structure + * address - Register address + * data - The data to send + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_set_reg(FAR struct lan9250_driver_s *priv, + uint16_t address, + uint32_t data) +{ +#ifdef CONFIG_LAN9250_SPI + uint8_t cmd_buffer[3]; + + DEBUGASSERT(priv && priv->spi); + + cmd_buffer[0] = LAN9250_SPI_WRITE; + cmd_buffer[1] = (uint8_t)(address >> 8); + cmd_buffer[2] = (uint8_t)(address >> 0); + + /* Select LAN9250 chip */ + + SPI_SELECT(priv->spi, SPIDEV_ETHERNET(0), true); + + /* Send the read command and register address */ + + SPI_SNDBLOCK(priv->spi, cmd_buffer, sizeof(cmd_buffer)); + + /* Receive register value, total 4 bytes */ + + SPI_SNDBLOCK(priv->spi, &data, 4); + + /* De-select LAN9250 chip. */ + + SPI_SELECT(priv->spi, SPIDEV_ETHERNET(0), false); +#else + struct qspi_cmdinfo_s cmdinfo; + int ret; + + cmdinfo.cmd = LAN9250_SPI_WRITE; + cmdinfo.addr = address; + cmdinfo.addrlen = sizeof(address); + cmdinfo.buffer = &data; + cmdinfo.buflen = sizeof(data); + cmdinfo.flags = QSPICMD_ADDRESS | QSPICMD_WRITEDATA; + if (priv->sqi_mode) + { + cmdinfo.flags |= QSPICMD_IQUAD; + } + + ret = QSPI_COMMAND(priv->qspi, &cmdinfo); + DEBUGASSERT(ret == 0); +#endif + + lan9250_setreg_dump(address, data); +} + +/**************************************************************************** + * Name: lan9250_wait_ready + * + * Description: + * Wait until LAN9250 is ready. + * + * Input Parameters: + * priv - Reference to the driver state structure + * address - Register address + * mask - Register value mask + * expected - Expected register value + * second - Wait timeout in second + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_wait_ready(FAR struct lan9250_driver_s *priv, + uint16_t address, uint32_t mask, + uint32_t expected, uint32_t second) +{ + clock_t tout_ticks = clock_systime_ticks() + SEC2TICK(second); + bool timeout = false; + + while (1) + { + if ((lan9250_get_reg(priv, address) & mask) == expected) + { + break; + } + else if (clock_systime_ticks() > tout_ticks) + { + timeout = true; + break; + } + } + + if (timeout) + { + nerr("ERROR: wait register:0x%02x, mask:0x%08x, expected:0x%08x\n", + address, mask, expected); + } +} + +/**************************************************************************** + * Name: lan9250_get_macreg + * + * Description: + * Read a MAC register value. + * + * Input Parameters: + * priv - Reference to the driver state structure + * address - MAC register address + * + * Returned Value: + * The value read from the MAC register. + * + ****************************************************************************/ + +static uint32_t lan9250_get_macreg(FAR struct lan9250_driver_s *priv, + uint8_t address) +{ + uint32_t regval = address | HMCSRICR_CSRB | HMCSRICR_RNW; + + /* Wait for MAC to be ready and send reading register command */ + + lan9250_wait_ready(priv, LAN9250_HMCSRICR, HMCSRICR_CSRB, 0, + LAN9250_MAC_TIMEOUT); + lan9250_set_reg(priv, LAN9250_HMCSRICR, regval); + + /* Wait for MAC to be ready and read register value */ + + lan9250_wait_ready(priv, LAN9250_HMCSRICR, HMCSRICR_CSRB, 0, + LAN9250_MAC_TIMEOUT); + regval = lan9250_get_reg(priv, LAN9250_HMCSRIDR); + + lan9250_getmacreg_dump(address, regval); + return regval; +} + +/**************************************************************************** + * Name: lan9250_set_macreg + * + * Description: + * Write to a MAC register. + * + * Input Parameters: + * priv - Reference to the driver state structure + * cmd - MAC register address + * data - The data to send + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_set_macreg(FAR struct lan9250_driver_s *priv, + uint8_t address, uint32_t data) +{ + uint32_t regval = address | HMCSRICR_CSRB; + + lan9250_setmacreg_dump(address, data); + + /* Wait for MAC to be ready and send writing register command and data */ + + lan9250_wait_ready(priv, LAN9250_HMCSRICR, HMCSRICR_CSRB, 0, + LAN9250_MAC_TIMEOUT); + lan9250_set_reg(priv, LAN9250_HMCSRIDR, data); + lan9250_set_reg(priv, LAN9250_HMCSRICR, regval); + + /* Wait until writing MAC is done */ + + lan9250_wait_ready(priv, LAN9250_HMCSRICR, HMCSRICR_CSRB, 0, + LAN9250_MAC_TIMEOUT); +} + +/**************************************************************************** + * Name: lan9250_wait_mac_ready + * + * Description: + * Wait until LAN9250 MAC is ready + * + * Input Parameters: + * priv - Reference to the driver state structure + * address - MAC register address + * mask - MAC register value mask + * expected - Expected MAC register value + * timeout - Wait timeout in second + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_wait_mac_ready(FAR struct lan9250_driver_s *priv, + uint8_t address, uint32_t mask, + uint32_t expected, uint32_t second) +{ + clock_t tout_ticks = clock_systime_ticks() + SEC2TICK(second); + bool timeout = false; + + while (1) + { + if ((lan9250_get_macreg(priv, address) & mask) == expected) + { + break; + } + else if (clock_systime_ticks() > tout_ticks) + { + timeout = true; + break; + } + } + + if (timeout) + { + nerr("ERROR: wait MAC register:0x%02x, mask:0x%08x, expected:0x%08x\n", + address, mask, expected); + } +} + +/**************************************************************************** + * Name: lan9250_get_phyreg + * + * Description: + * Read 16-bits of PHY data. + * + * Input Parameters: + * priv - Reference to the driver state structure + * address - The PHY register address + * + * Returned Value: + * 16-bit value read from the PHY + * + ****************************************************************************/ + +static uint16_t lan9250_get_phyreg(FAR struct lan9250_driver_s *priv, + uint8_t address) +{ + uint32_t regval = (1 << HMACMIIAR_PHYA_S) | + (address << HMACMIIAR_MIIRX_S); + + /* Wait PHY to be ready and send reading register command */ + + lan9250_wait_mac_ready(priv, LAN9250_HMACMIIAR, HMACMIIAR_MIIB, 0, + LAN9250_PHY_TIMEOUT); + lan9250_set_macreg(priv, LAN9250_HMACMIIAR, regval); + + /* Wait PHY to be ready and read register value */ + + lan9250_wait_mac_ready(priv, LAN9250_HMACMIIAR, HMACMIIAR_MIIB, 0, + LAN9250_PHY_TIMEOUT); + regval = lan9250_get_macreg(priv, LAN9250_HMACMIIDR); + + lan9250_getphyreg_dump(address, regval); + + return regval; +} + +/**************************************************************************** + * Name: lan9250_set_phyreg + * + * Description: + * Write 16-bits of PHY data. + * + * Input Parameters: + * priv - Reference to the driver state structure + * address - The PHY register address + * data - 16-bit data to write to the PHY + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_set_phyreg(FAR struct lan9250_driver_s *priv, + uint8_t address, + uint16_t data) +{ + uint32_t regval = (1 << HMACMIIAR_PHYA_S) | + (address << HMACMIIAR_MIIRX_S) | + HMACMIIAR_MIIW; + + DEBUGASSERT((address & (HMACMIIAR_MIIRX_M >> HMACMIIAR_MIIRX_S)) + == address); + + lan9250_setphyreg_dump(address, data); + + /* Wait PHY to be ready and send writing register command and data */ + + lan9250_wait_mac_ready(priv, LAN9250_HMACMIIAR, HMACMIIAR_MIIB, 0, + LAN9250_PHY_TIMEOUT); + lan9250_set_macreg(priv, LAN9250_HMACMIIDR, data); + lan9250_set_macreg(priv, LAN9250_HMACMIIAR, regval); + + /* Wait PHY until writing is done */ + + lan9250_wait_mac_ready(priv, LAN9250_HMACMIIAR, HMACMIIAR_MIIB, 0, + LAN9250_PHY_TIMEOUT); +} + +/**************************************************************************** + * Name: lan9250_recv_buffer + * + * Description: + * Read a buffer of data. + * + * Input 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 + * + ****************************************************************************/ + +static void lan9250_recv_buffer(FAR struct lan9250_driver_s *priv, + FAR uint8_t *buffer, + size_t buflen) +{ +#ifdef CONFIG_LAN9250_SPI +# if LAN9250_SPI_READ == LAN9250_SPI_CMD_FREAD + uint8_t cmd_buffer[4]; +# else + uint8_t cmd_buffer[3]; +# endif + + DEBUGASSERT(priv && priv->spi); + DEBUGASSERT(buflen <= sizeof(priv->pktbuf)); + + /* Read dummy data */ + + lan9250_get_reg(priv, LAN9250_RXDFR); + + /* Read valid buffer data */ + + cmd_buffer[0] = LAN9250_SPI_READ; + cmd_buffer[1] = (uint8_t)(LAN9250_RXDFR >> 8); + cmd_buffer[2] = (uint8_t)(LAN9250_RXDFR >> 0); +# if LAN9250_SPI_READ == LAN9250_SPI_CMD_FREAD + cmd_buffer[3] = 0xff; /* SPI read dummy */ +# endif + + /* Select LAN9250 chip */ + + SPI_SELECT(priv->spi, SPIDEV_ETHERNET(0), true); + + /* Send the read command and register address */ + + SPI_SNDBLOCK(priv->spi, cmd_buffer, sizeof(cmd_buffer)); + SPI_RECVBLOCK(priv->spi, buffer, buflen); + + /* De-select LAN9250 chip */ + + SPI_SELECT(priv->spi, SPIDEV_ETHERNET(0), false); +#else + struct qspi_meminfo_s meminfo; + int ret; + + meminfo.cmd = LAN9250_SPI_READ; + meminfo.addr = LAN9250_RXDFR; + meminfo.addrlen = sizeof(uint16_t); + meminfo.buffer = buffer; + meminfo.buflen = buflen; + meminfo.dummies = 16; /* 8 SPI dummy clock and 4 dummy RX bytes */ + meminfo.flags = QSPIMEM_READ; + if (priv->sqi_mode) + { + meminfo.flags |= QSPIMEM_IQUAD; + } + + ret = QSPI_MEMORY(priv->qspi, &meminfo); + DEBUGASSERT(ret == 0); +#endif /* CONFIG_LAN9250_SPI */ + + lan9250_buffer_dump(LAN9250_RXDFR, buffer, buflen); +} + +/**************************************************************************** + * Name: lan9250_send_buffer + * + * Description: + * Write a buffer of data. + * + * Input 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 + * + ****************************************************************************/ + +static inline void lan9250_send_buffer(FAR struct lan9250_driver_s *priv, + FAR const uint8_t *buffer, + size_t buflen) +{ +#ifdef CONFIG_LAN9250_SPI + uint8_t cmd_buffer[3]; + uint32_t regval; +#else + struct qspi_meminfo_s meminfo; + uint32_t regval; + int ret; +#endif + + lan9250_buffer_dump(LAN9250_TXDFR, buffer, buflen); + + /** + * LAN9250 SPI write command A fields: + * + * - TX packet length 4 bytes align + * - First frame + * - Last frame + */ + + regval = SPI_CMD_WRITE_A_4BA | SPI_CMD_WRITE_A_FS | + SPI_CMD_WRITE_A_LS | buflen; + lan9250_set_reg(priv, LAN9250_TXDFR, regval); + + /** + * LAN9250 SPI write command B fields: + * + * - Packet TAG + */ + + regval = SPI_CMD_WRITE_B_PT | buflen; + lan9250_set_reg(priv, LAN9250_TXDFR, regval); + +#ifdef CONFIG_LAN9250_SPI + + /* Read valid buffer data */ + + cmd_buffer[0] = LAN9250_SPI_WRITE; + cmd_buffer[1] = (uint8_t)(LAN9250_TXDFR >> 8); + cmd_buffer[2] = (uint8_t)(LAN9250_TXDFR >> 0); + + /* Select LAN9250 chip */ + + SPI_SELECT(priv->spi, SPIDEV_ETHERNET(0), true); + + /* Send the read command and register address, total 3 bytes */ + + SPI_SNDBLOCK(priv->spi, cmd_buffer, 3); + + /* Write data into FIFO with 4-byte align */ + + SPI_SNDBLOCK(priv->spi, buffer, LAN9250_ALIGN(buflen)); + + /* De-select LAN9250 chip */ + + SPI_SELECT(priv->spi, SPIDEV_ETHERNET(0), false); +#else + meminfo.cmd = LAN9250_SPI_WRITE; + meminfo.addr = LAN9250_TXDFR; + meminfo.addrlen = sizeof(uint16_t); + meminfo.buffer = (void *)buffer; + meminfo.buflen = LAN9250_ALIGN(buflen); + meminfo.dummies = 0; + meminfo.flags = QSPIMEM_WRITE; + if (priv->sqi_mode) + { + meminfo.flags |= QSPIMEM_IQUAD; + } + + ret = QSPI_MEMORY(priv->qspi, &meminfo); + DEBUGASSERT(ret == 0); +#endif /* CONFIG_LAN9250_SPI */ +} + +/**************************************************************************** + * Name: lan9250_enable_sqi + * + * Description: + * Send a command to LAN9250 to enable SQI mode. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_LAN9250_SQI +static void lan9250_enable_sqi(FAR struct lan9250_driver_s *priv) +{ + clock_t tout_ticks = clock_systime_ticks() + + MSEC2TICK(LAN9250_SQI_TIMEOUT); + struct qspi_cmdinfo_s cmdinfo; + int ret; + + cmdinfo.cmd = LAN9250_SPI_ENABLE_SQI; + cmdinfo.addrlen = 0; + cmdinfo.buflen = 0; + cmdinfo.flags = 0; + + ret = QSPI_COMMAND(priv->qspi, &cmdinfo); + DEBUGASSERT(ret == 0); + + /* USE SQI mode interface */ + + priv->sqi_mode = true; + + /* Check read register value in SQI mode */ + + while (1) + { + if ((lan9250_get_reg(priv, LAN9250_BOTR) & BOTR_MASK) == BOTR_VAL) + { + break; + } + else if (clock_systime_ticks() > tout_ticks) + { + /* Check timeout and continue to use SPI mode */ + + priv->sqi_mode = false; + break; + } + } +} +#endif + +/**************************************************************************** + * Name: lan9250_sw_reset + * + * Description: + * Send a command to reset LAN9250. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void lan9250_sw_reset(FAR struct lan9250_driver_s *priv) +{ + /* Wait until LAN9250 SPI bus is ready */ + + lan9250_wait_ready(priv, LAN9250_BOTR, BOTR_MASK, + BOTR_VAL, LAN9250_RESET_TIMEOUT); + + /** + * Send a command to reset: + * + * - Digital controller + * - HMAC + * - PHY + */ + + lan9250_set_reg(priv, LAN9250_RSTCR, RSTCR_DR | RSTCR_PHYR | RSTCR_HMACR); + + /* Wait some time for hardware ready */ + + lan9250_wait_ready(priv, LAN9250_HWCFGR, HWCFGR_READY, + HWCFGR_READY, LAN9250_RESET_TIMEOUT); + +#ifdef CONFIG_LAN9250_SQI + + /* Enable SQI mode */ + + lan9250_enable_sqi(priv); +#endif +} + +/**************************************************************************** + * Name: lan9250_set_txavailabe + * + * Description: + * Enable or disable TX data FIFO available interrupt. + * + * Input Parameters: + * priv - Reference to the driver state structure + * enable - true: enable this interrupt, false: disable this interrupt + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_set_txavailabe(FAR struct lan9250_driver_s *priv, + bool enable) +{ + uint32_t regval; + + if (enable) + { + /** + * Configure FIFO level interrupt: + * + * - TX data available level: Ethernet maximum packet size, transform + * this value to be block number with 64-byte align + * - TX status level: 255, use maximum value so that this will not be + * actually used by this driver + * - RX status level: 0, so that RX interrupt can trigger once + * receiving one packet + */ + + regval = (((LAN9250_PKTBUF_SIZE + 63) / 64) << FLIR_FITXDAL_S) | + FLIR_FITXSL_M; + + /* Mark TX data FIFO is unavailable */ + + priv->tx_available = false; + } + else + { + /** + * Configure FIFO level interrupt: + * + * - TX data available level: 255, so that no interrupt triggers + * - TX status level: 255, use maximum value so that this will not be + * actually used by this driver + * - RX status level: 0, so that RX interrupt can trigger once + * receiving one packet + */ + + regval = FLIR_FITXDAL_M | FLIR_FITXSL_M; + + /* Mark TX data FIFO is available */ + + priv->tx_available = true; + } + + lan9250_set_reg(priv, LAN9250_FLIR, regval); +} + +/**************************************************************************** + * Name: lan9250_reset + * + * Description: + * Stop, reset, re-initialize, and restart the LAN9250. This is done + * initially, on ifup, and after a TX timeout. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +static int lan9250_reset(FAR struct lan9250_driver_s *priv) +{ + uint32_t regval; + + nwarn("WARNING: Reset\n"); + + /* Configure SPI for the LAN9250 */ + + lan9250_config_spi(priv); + + /* reset the LAN9250 by sending command */ + + lan9250_sw_reset(priv); + + /* Read LAN9250 hardware ID */ + + regval = lan9250_get_reg(priv, LAN9250_CIARR); + if ((regval & CIARR_CID_M) != CIARR_CID_V) + { + nerr("ERROR: Bad Rev ID: %08x\n", regval); + return -ENODEV; + } + + ninfo("Rev ID: %08x\n", regval & CIARR_CREV_M); + + /** + * Configure TX FIFO size mode to be 8: + * + * - TX data FIFO size: 7680 + * - RX data FIFO size: 7680 + * - TX status FIFO size: 512 + * - RX status FIFO size: 512 + */ + + regval = HWCFGR_MBO | (8 << HWCFGR_TXFS_S); + lan9250_set_reg(priv, LAN9250_HWCFGR, regval); + + /** + * Configure MAC automatic flow control: + * + * - Automatic flow control high level: 110 + * - Automatic flow control low level: 55 + * - Backpressure duration: 4 + * - Flow control on any frame + */ + + regval = (110 << HMAFCCFGR_AFCHL_S) | + (55 << HMAFCCFGR_AFCLL_S) | + (4 << HMAFCCFGR_BPD_S) | + HMAFCCFGR_FCOAF; + lan9250_set_reg(priv, LAN9250_HMAFCCFGR, regval); + + /** + * Configure host MAC flow control: + * + * - Pause time: 15 + * - Flow control + */ + + regval = (0xf << HMACFCR_PT_S) | HMACFCR_FLE; + lan9250_set_macreg(priv, LAN9250_HMACFCR, regval); + + /** + * Configure interrupt: + * + * - Interrupt De-assertion interval: 10 + * - Interrupt output to pin + * - Interrupt pin active output low + * - Interrupt pin push-pull driver + */ + + regval = (10 << ICFGR_IDAI_S) | + ICFGR_IRQE | ICFGR_IRQBT; + lan9250_set_reg(priv, LAN9250_ICFGR, regval); + + /** + * Configure interrupt trigger source, please refer to macro + * LAN9250_INT_SOURCE. + */ + + lan9250_set_reg(priv, LAN9250_IER, LAN9250_INT_SOURCE); + + /* Disable TX data FIFO available interrupt */ + + lan9250_set_txavailabe(priv, false); + + /** + * Configure RX: + * + * - RX DMA counter: Ethernet maximum packet size + * - RX data offset: 4, so that need read dummy before reading data + */ + + regval = (LAN9250_PKTBUF_SIZE << RXCFGR_RXDMAC_S) | (4 << RXCFGR_RXDO_S); + lan9250_set_reg(priv, LAN9250_RXCFGR, regval); + + /** + * Configure remote power management: + * + * - Auto wakeup + * - Disable 1588 clock + * - Disable 1588 timestamp unit clock + * - Energy-detect + * - Wake on + * - PME pin push-pull driver + * - Clear wakeon + * - PME active high + * - PME pin + */ + + regval = PMCR_PMWU | PMCR_1588CLKD | PMCR_1588TSUCLKD | + PMCR_EDE | PMCR_WOE | PMCR_PMEBT | + PMCR_WOLS | PMCR_PMEP | PMCR_PMEE; + lan9250_set_reg(priv, LAN9250_PMCR, regval); + + /** + * Configure PHY basic control: + * + * - Auto-Negotiation for speed(10 or 100Mbsp) and direction + * (half or full duplex) + */ + + lan9250_set_phyreg(priv, LAN9250_PHYBCR, PHYBCR_ANE); + + /** + * Configure PHY auto-negotiation advertisement capability: + * + * - Asymmetric pause + * - Symmetric pause + * - 100Base-X full deplex if !CONFIG_LAN9250_HALFDUPPLEX + * - 100Base-X half deplex + * - 10Base-X full deplex if !CONFIG_LAN9250_HALFDUPPLEX + * - 10Base-X half deplex + * - Select IEEE802.3 + */ + + regval = PHYANAR_AP | PHYANAR_SP | PHYANAR_100BXHD | + PHYANAR_10BXHD | PHYANAR_SF; +#ifndef CONFIG_LAN9250_HALFDUPPLEX + regval |= PHYANAR_100BXFD | PHYANAR_10BXFD; +#endif + lan9250_set_phyreg(priv, LAN9250_PHYANAR, regval); + + /** + * Configure PHY special mode: + * + * - PHY mode = 111b, enable all capable and auto-nagotiation + * - PHY address = 1, default value is fixed to 1 by manufacturer + */ + + regval = PHYSMR_PM_M | 1; + lan9250_set_phyreg(priv, LAN9250_PHYSMR, regval); + + /** + * Configure PHY special control or status indication: + * + * - Port auto-MDIX determined by bits 14 and 13 + * - Auto-MDIX + * - Disable SQE tests + */ + + regval = PHYSCOSIR_AMDIXC | PHYSCOSIR_AMDIXE | PHYSCOSIR_SQETD; + lan9250_set_phyreg(priv, LAN9250_PHYSCOSIR, regval); + + /** + * Configure PHY interrupt source: + * + * - Link up + * - Link down + */ + + regval = PHYIER_LU | PHYIER_LD; + lan9250_set_phyreg(priv, LAN9250_PHYIER, regval); + + /** + * Configure special control or status: + * + * - Fixed to write 0000010b to reserved filed + */ + + lan9250_set_phyreg(priv, LAN9250_PHYSCOSR, PHYSCOSR_RD); + + /* Clear interrupt status */ + + lan9250_set_reg(priv, LAN9250_ISR, UINT32_MAX); + + /** + * Configure HMAC control: + * + * - Automaticaly strip the pad field on incoming packets + * - TX enable + * - RX enable + * - Full duplex mode if !CONFIG_LAN9250_HALFDUPPLEX + */ + + regval = HMACCR_APS | HMACCR_TXE | HMACCR_RXE; +#ifndef CONFIG_LAN9250_HALFDUPPLEX + regval |= HMACCR_FDM; +#endif + lan9250_set_macreg(priv, LAN9250_HMACCR, regval); + + /** Configure TX: + * + * - TX enable + */ + + lan9250_set_reg(priv, LAN9250_TXCFGR, TXCFGR_TXE); + + return OK; +} + +/**************************************************************************** + * Name: lan9250_set_macaddr + * + * 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. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_set_macaddr(FAR struct lan9250_driver_s *priv) +{ + uint32_t high_addr; + uint32_t low_addr; + FAR uint8_t *mac = priv->dev.d_mac.ether.ether_addr_octet; + + high_addr = (((uint32_t)mac[5] << 8) | ((uint32_t)mac[4] << 0)); + low_addr = (((uint32_t)mac[3] << 24) | ((uint32_t)mac[2] << 16) | + ((uint32_t)mac[1] << 8) | ((uint32_t)mac[0] << 0)); + + lan9250_set_macreg(priv, LAN9250_HMACAHR, high_addr); + lan9250_set_macreg(priv, LAN9250_HMACALR, low_addr); +} + +/**************************************************************************** + * Name: lan9250_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. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + ****************************************************************************/ + +static int lan9250_transmit(FAR struct lan9250_driver_s *priv) +{ + uint32_t regval; + uint16_t free_size; + uint8_t status_size; + + regval = lan9250_get_reg(priv, LAN9250_TXFIR); + status_size = (regval & TXFIR_TXSFUS_M) >> TXFIR_TXSFUS_S; + free_size = regval & TXFIR_TXDFFS_M; + + ninfo("availabe status size:%d, free space size:%d\n", + status_size, free_size); + + /* Clear TX status FIFO if it is no empty by reading data */ + + for (int i = 0; i < status_size; i++) + { + lan9250_get_reg(priv, LAN9250_TXSFR); + } + + if (free_size > priv->dev.d_len) + { + /* Increment statistics */ + + ninfo("Sending packet, pktlen: %d\n", priv->dev.d_len); + NETDEV_TXPACKETS(&priv->dev); + + /* Send the packet: address=priv->dev.d_buf, length=priv->dev.d_len */ + + lan9250_dump_buf("Transmit Packet", priv->dev.d_buf, + priv->dev.d_len); + + lan9250_send_buffer(priv, priv->dev.d_buf, priv->dev.d_len); + + if ((free_size - priv->dev.d_len) < LAN9250_PKTBUF_SIZE) + { + /* Enable TX data FIFO available interrupt */ + + lan9250_set_txavailabe(priv, true); + + /** + * Enable the TX timeout watchdog (perhaps restarting the timer) + * when free data space is not enough. + */ + + wd_start(&priv->txtout_timer, LAN9250_TX_TIMEOUT, + lan9250_txtout_timercb, (wdparm_t)priv); + } + } + else + { + /* This condition is not supported. */ + + DEBUGASSERT(0); + } + + return OK; +} + +/**************************************************************************** + * Name: lan9250_txpoll + * + * Description: + * The transmitter is available, check if the network has any outgoing + * packets ready to send. This is a callback from devif_poll(). + * devif_poll() may be called: + * + * 1. When the preceding TX packet send is complete, + * 2. When the preceding TX packet send timesout and the interface is + * reset + * 3. During normal TX polling + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + ****************************************************************************/ + +static int lan9250_txpoll(FAR struct net_driver_s *dev) +{ + FAR struct lan9250_driver_s *priv = + (FAR struct lan9250_driver_s *)dev->d_private; + + /* Send the packet */ + + lan9250_transmit(priv); + + /* Stop the poll now if free FIFO buffer is not enough */ + + return priv->tx_available ? OK : -EBUSY; +} + +/**************************************************************************** + * Name: lan9250_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Parameters: + * dev - The reference to the driver structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_txavail_work(FAR void *arg) +{ + FAR struct lan9250_driver_s *priv = (FAR struct lan9250_driver_s *)arg; + FAR struct net_driver_s *dev = &priv->dev; + + /* Lock the network and serialize driver operations if necessary. + * NOTE: Serialization is only required in the case where the driver work + * is performed on an LP worker thread and where more than one LP worker + * thread has been configured. + */ + + net_lock(); + lan9250_lock_spi(priv); + + /* Ignore the notification if the interface is not yet up */ + + if (IFF_IS_UP(dev->d_flags)) + { + /* Check if there is room in the hardware to hold another outgoing + * packet. + */ + + /* If so, then poll the network for new XMIT data */ + + devif_poll(dev, lan9250_txpoll); + } + + /* Release lock on the SPI bus and the network */ + + lan9250_unlock_spi(priv); + net_unlock(); +} + +/**************************************************************************** + * Name: lan9250_phy_isr + * + * Description: + * Process LAN9250 PHY interrupt. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_phy_isr(FAR struct lan9250_driver_s *priv) +{ + uint16_t regval; + + regval = lan9250_get_phyreg(priv, LAN9250_PHYISFR); + if (regval & PHYISFR_LU) + { + ninfo("Link up\n"); + + IFF_SET_UP(priv->dev.d_flags); + } + else if (regval & PHYISFR_LD) + { + ninfo("Link down\n"); + + IFF_CLR_UP(priv->dev.d_flags); + } +} + +/**************************************************************************** + * Name: lan9250_txavailable_isr + * + * Description: + * TX data FIFO available interrupt function. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_txavailable_isr(FAR struct lan9250_driver_s *priv) +{ + /* Update statistics */ + + NETDEV_TXDONE(&priv->dev); + + /* Disable TX data FIFO available interrupt */ + + lan9250_set_txavailabe(priv, false); + + /* If no further xmits are pending, then cancel the TX timeout */ + + wd_cancel(&priv->txtout_timer); + + /* Then poll the network for new XMIT data */ + + devif_poll(&priv->dev, lan9250_txpoll); +} + +/**************************************************************************** + * Name: lan9250_netdev_rx + * + * Description: + * Give the newly received packet to the network. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_netdev_rx(FAR struct lan9250_driver_s *priv) +{ +#ifdef CONFIG_NET_PKT + /* When packet sockets are enabled, feed the frame into the tap */ + + pkt_input(&priv->dev); +#endif + + /* We only accept IP packets of the configured type and ARP packets */ + +#ifdef CONFIG_NET_IPv4 + if (BUF->type == HTONS(ETHTYPE_IP)) + { + ninfo("IPv4 frame\n"); + NETDEV_RXIPV4(&priv->dev); + + /* Receive an IPv4 packet from the network device */ + + ipv4_input(&priv->dev); + + /* If the above function invocation resulted in data that should be + * sent out on the network, d_len field will set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* And send the packet */ + + lan9250_transmit(priv); + } + } + else +#endif /* CONFIG_NET_IPv4 */ +#ifdef CONFIG_NET_IPv6 + if (BUF->type == HTONS(ETHTYPE_IP6)) + { + ninfo("IPv6 frame\n"); + NETDEV_RXIPV6(&priv->dev); + + /* Give the IPv6 packet to the network layer */ + + ipv6_input(&priv->dev); + + /* If the above function invocation resulted in data that should be + * sent out on the network, d_len field will set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* And send the packet */ + + lan9250_transmit(priv); + } + } + else +#endif /* CONFIG_NET_IPv6 */ +#ifdef CONFIG_NET_ARP + if (BUF->type == HTONS(ETHTYPE_ARP)) + { + ninfo("ARP packet received (%02x)\n", BUF->type); + NETDEV_RXARP(&priv->dev); + + arp_input(&priv->dev); + + /* If the above function invocation resulted in data that should be + * sent out on the network, d_len field will set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + lan9250_transmit(priv); + } + } + else +#endif /* CONFIG_NET_ARP */ + { + nwarn("WARNING: Unsupported packet type dropped (%02x)\n", + HTONS(BUF->type)); + NETDEV_RXDROPPED(&priv->dev); + } +} + +/**************************************************************************** + * Name: lan9250_rxdone_isr + * + * Description: + * An interrupt was received indicating the availability of a new RX packet + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_rxdone_isr(FAR struct lan9250_driver_s *priv) +{ + uint8_t pktcnt; + uint16_t pktlen; + uint32_t regval; + + /* Update statistics */ + + NETDEV_RXPACKETS(&priv->dev); + + /* Check valiad packet count */ + + regval = lan9250_get_reg(priv, LAN9250_RXFIR); + pktcnt = (regval & RXFIR_RXSFUS_M) >> RXFIR_RXSFUS_S; + + ninfo("pktcnt:%d\n", pktcnt); + + while (pktcnt--) + { + /* Check valiad packet data size */ + + regval = lan9250_get_reg(priv, LAN9250_RXSFR); + pktlen = (regval & RXSFF_PL_M) >> RXSFF_PL_S; + + ninfo("pktlen:%d\n", pktlen); + + if (pktlen) + { + /** + * Copy the data from the receive buffer to priv->dev.d_buf. + * ERDPT should be correctly positioned from the last call to + * end_rdbuffer (above). + */ + + lan9250_recv_buffer(priv, priv->dev.d_buf, pktlen); + + /** + * Save the packet length (without the 4 byte CRC) + * in priv->dev.d_len. + */ + + priv->dev.d_len = pktlen - 4; + + lan9250_dump_buf("Received Packet", priv->dev.d_buf, + priv->dev.d_len); + + /* Dispatch the packet to the network */ + + lan9250_netdev_rx(priv); + } + } +} + +/**************************************************************************** + * Name: lan9250_int_worker + * + * Description: + * Perform interrupt handling logic outside of the interrupt handler (on + * the work queue thread). + * + * Input Parameters: + * arg - The reference to the driver structure (case to void*) + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_int_worker(FAR void *arg) +{ + FAR struct lan9250_driver_s *priv = (FAR struct lan9250_driver_s *)arg; + uint32_t regval; + + DEBUGASSERT(priv); + + /* Get exclusive access to both the network and the SPI bus. */ + + net_lock(); + lan9250_lock_spi(priv); + + /** + * There is no infinite loop check... if there are always pending + * interrupts, we are just broken. + */ + + regval = lan9250_get_reg(priv, LAN9250_ISR); + if ((regval & LAN9250_INT_SOURCE) != 0) + { + /* Handle interrupts according to interrupt resource bit + * settings. + */ + + ninfo("Interrupt status: %08x\n", regval); + +#if LAN9250_INT_SOURCE & IER_SW + if ((regval & ISR_SW) != 0) + { + ninfo("\tSoftware\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_RAEDY + if ((regval & ISR_READY) != 0) + { + ninfo("\tDevice ready\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_1588 + if ((regval & ISR_1588) != 0) + { + ninfo("\t1588\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_PHY + if ((regval & ISR_PHY) != 0) + { + ninfo("\tPHY\n"); + + lan9250_phy_isr(priv); + } +#endif + +#if LAN9250_INT_SOURCE & IER_TXSTOP + if ((regval & ISR_TXS) != 0) + { + ninfo("\tTX stop\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_RXSTOP + if ((regval & ISR_RXS) != 0) + { + ninfo("\tRX stop\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_RXDFH + if ((regval & ISR_RXDFH) != 0) + { + ninfo("\tRX dropped frame count halfway\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_TXIOC + if ((regval & ISR_TXIOC) != 0) + { + ninfo("\tTX IOC\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_RXD + if ((regval & ISR_RXD) != 0) + { + ninfo("\tRX DMA\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_GPT + if ((regval & ISR_GPT) != 0) + { + ninfo("\tGP-timer\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_PME + if ((regval & ISR_PME) != 0) + { + ninfo("\tPower management\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_TXSO + if ((regval & ISR_TXSO) != 0) + { + ninfo("\tTX FIFO status overflow\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_RWT + if ((regval & ISR_RWT) != 0) + { + ninfo("\tRX watchdog timeout\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_RXE + if ((regval & ISR_RXE) != 0) + { + ninfo("\tRX error\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_TXE + if ((regval & ISR_TXE) != 0) + { + ninfo("\tTX error\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_GPIO + if ((regval & ISR_GPIO) != 0) + { + ninfo("\tISR_GPIO event\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_TDFO + if ((regval & ISR_TDFO) != 0) + { + ninfo("\tTX data FIFO overrun\n"); + } +#endif + + if ((regval & ISR_TDFA) != 0) + { + ninfo("\tTX data FIFO available\n"); + + lan9250_txavailable_isr(priv); + } + +#if LAN9250_INT_SOURCE & IER_TSFF + if ((regval & ISR_TSFF) != 0) + { + ninfo("\tTX status FIFO full\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_TSFL + if ((regval & ISR_TSFL) != 0) + { + ninfo("\tTX status FIFO level\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_RXDF + if ((regval & ISR_RXDF) != 0) + { + ninfo("\tRX dropped frame\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_RSFF + if ((regval & ISR_RSFF) != 0) + { + ninfo("\tRX status FIFO full\n"); + } +#endif + +#if LAN9250_INT_SOURCE & IER_RSFL + if ((regval & ISR_RSFL) != 0) + { + ninfo("\tRX status FIFO level\n"); + + lan9250_rxdone_isr(priv); + } +#endif + + lan9250_set_reg(priv, LAN9250_ISR, regval); + } + + /* Release lock on the SPI bus and the network */ + + lan9250_unlock_spi(priv); + net_unlock(); + + /** + * Enable ISR_GPIO interrupts after unlocking net so that application + * could have chance to process Ethernet packet and free iob. + */ + + priv->lower->enable(priv->lower); +} + +/**************************************************************************** + * Name: lan9250_interrupt + * + * Description: + * Hardware interrupt handler + * + * Input Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info (architecture-specific) + * arg - Interrupt private data + * + * Returned Value: + * OK on success + * + ****************************************************************************/ + +static int lan9250_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct lan9250_driver_s *priv; + + DEBUGASSERT(arg != NULL); + priv = (FAR struct lan9250_driver_s *)arg; + + /* 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->irq_work)); + + /* Notice that further ISR_GPIO interrupts are disabled until the work is + * actually performed. This is to prevent overrun of the worker thread. + * Interrupts are re-enabled in lan9250_int_worker() when the work is done. + */ + + priv->lower->disable(priv->lower); + return work_queue(LAN9250_WORK, &priv->irq_work, lan9250_int_worker, + (void *)priv, 0); +} + +/**************************************************************************** + * Name: lan9250_txtout_worker + * + * Description: + * Our TX watchdog timed out. This is the worker thread continuation of + * the watchdog timer interrupt. Reset the hardware and start again. + * + * Input Parameters: + * arg - The reference to the driver structure (case to void*) + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_txtout_worker(FAR void *arg) +{ + FAR struct lan9250_driver_s *priv = (FAR struct lan9250_driver_s *)arg; + int ret; + + nerr("ERROR: Tx timeout\n"); + DEBUGASSERT(priv); + + /* Get exclusive access to the network */ + + net_lock(); + + /* Increment statistics and dump debug info */ + + NETDEV_TXTIMEOUTS(&priv->dev); + + /* Then reset the hardware: Take the interface down, then bring it + * back up + */ + + ret = lan9250_ifdown(&priv->dev); + DEBUGASSERT(ret == OK); + ret = lan9250_ifup(&priv->dev); + DEBUGASSERT(ret == OK); + UNUSED(ret); + + /* Then poll the network for new XMIT data */ + + devif_poll(&priv->dev, lan9250_txpoll); + + /* Release lock on the network */ + + net_unlock(); +} + +/**************************************************************************** + * Name: lan9250_txtout_timercb + * + * Description: + * Our TX watchdog timed out. Called from the timer interrupt handler. + * The last TX never completed. Perform work on the worker thread. + * + * Input Parameters: + * arg - The argument + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lan9250_txtout_timercb(wdparm_t arg) +{ + FAR struct lan9250_driver_s *priv = (FAR struct lan9250_driver_s *)arg; + int ret; + + /* In complex environments, we cannot do SPI transfers from the timeout + * 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->txtout_work)); + + /* Notice that Tx timeout watchdog is not active so further Tx timeouts + * can occur until we restart the Tx timeout watchdog. + */ + + ret = work_queue(LAN9250_WORK, &priv->txtout_work, + lan9250_txtout_worker, priv, 0); + DEBUGASSERT(ret == OK); + UNUSED(ret); +} + +/**************************************************************************** + * Name: lan9250_ifup + * + * Description: + * NuttX Callback: Bring up the Ethernet interface when an IP address is + * provided + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +static int lan9250_ifup(FAR struct net_driver_s *dev) +{ + FAR struct lan9250_driver_s *priv = + (FAR struct lan9250_driver_s *)dev->d_private; + int ret; +#ifdef CONFIG_LAN9250_REGDEBUG + uint32_t mac_addr[2]; +#endif + + ninfo("Bringing up: %u.%u.%u.%u\n", + ip4_addr1(dev->d_ipaddr), ip4_addr2(dev->d_ipaddr), + ip4_addr3(dev->d_ipaddr), ip4_addr4(dev->d_ipaddr)); + + /* Lock the SPI bus so that we have exclusive access */ + + lan9250_lock_spi(priv); + + /* Initialize Ethernet interface, set the MAC address, and make sure that + * the LAN9250 is not in power save mode. + */ + + ret = lan9250_reset(priv); + if (ret == OK) + { + lan9250_set_macaddr(priv); + + /* Enable interrupts at the LAN9250. Interrupts are still disabled + * at the interrupt controller. + */ + + /* Enable the receiver */ + + /* Mark the interface up and enable the Ethernet interrupt at the + * controller + */ + + priv->lower->enable(priv->lower); + + /* Read MAC address here in debug mode */ + +#ifdef CONFIG_LAN9250_REGDEBUG + mac_addr[0] = lan9250_get_macreg(priv, LAN9250_HMACALR); + mac_addr[1] = lan9250_get_macreg(priv, LAN9250_HMACAHR); + + ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + (uint8_t)(mac_addr[1] >> 0), (uint8_t)(mac_addr[1] >> 8), + (uint8_t)(mac_addr[0] >> 24), (uint8_t)(mac_addr[0] >> 16), + (uint8_t)(mac_addr[0] >> 8), (uint8_t)(mac_addr[0] >> 0)); +#endif + } + + /* Un-lock the SPI bus */ + + lan9250_unlock_spi(priv); + return ret; +} + +/**************************************************************************** + * Name: lan9250_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +static int lan9250_ifdown(FAR struct net_driver_s *dev) +{ + FAR struct lan9250_driver_s *priv = + (FAR struct lan9250_driver_s *)dev->d_private; + irqstate_t flags; + + ninfo("Taking down: %u.%u.%u.%u\n", + ip4_addr1(dev->d_ipaddr), ip4_addr2(dev->d_ipaddr), + ip4_addr3(dev->d_ipaddr), ip4_addr4(dev->d_ipaddr)); + + /* Lock the SPI bus so that we have exclusive access */ + + lan9250_lock_spi(priv); + + /* Reset the device and leave in the power save state */ + + lan9250_sw_reset(priv); + + /* Disable the Ethernet interrupt */ + + flags = enter_critical_section(); + priv->lower->disable(priv->lower); + + /* Cancel the TX timeout timers */ + + wd_cancel(&priv->txtout_timer); + + IFF_CLR_UP(priv->dev.d_flags); + leave_critical_section(flags); + + /* Un-lock the SPI bus */ + + lan9250_unlock_spi(priv); + return OK; +} + +/**************************************************************************** + * Name: lan9250_txavail + * + * Description: + * Driver callback invoked when new TX data is available. This is a + * stimulus perform an out-of-cycle poll and, thereby, reduce the TX + * latency. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +static int lan9250_txavail(FAR struct net_driver_s *dev) +{ + FAR struct lan9250_driver_s *priv = + (FAR struct lan9250_driver_s *)dev->d_private; + irqstate_t flags; + + /** + * Lock the SPI bus so that we have exclusive access for but SPI and + * LAN9250 SPI private data. + */ + + lan9250_lock_spi(priv); + + flags = enter_critical_section(); + + /** + * Since SPI is locked, so interrupt work must not really process when + * CPU run here, so: + * + * - priv->tx_available = true, TX data FIFO is available and its related + * interrupt is disable. + * - priv->tx_available = false, TX data FIFO is unavailable and its + * related interrupt is enable, so when this interrupt triggers, + * function "devif_poll" will poll the network for new XMIT data. + */ + + if (IFF_IS_UP(dev->d_flags) && + priv->tx_available && + work_available(&priv->txpoll_work)) + { + /* Schedule to serialize the poll on the worker thread. */ + + work_queue(LAN9250_WORK, &priv->txpoll_work, + lan9250_txavail_work, priv, 0); + } + + /* Un-lock the SPI bus */ + + leave_critical_section(flags); + lan9250_unlock_spi(priv); + return OK; +} + +/**************************************************************************** + * Name: lan9250_addmac + * + * Description: + * NuttX Callback: Add the specified MAC address to the hardware multicast + * address filtering + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be added + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_MCASTGROUP +static int lan9250_addmac(FAR struct net_driver_s *dev, + FAR const uint8_t *mac) +{ + /* #warning "Multicast MAC support not implemented" */ + + return -ENOSYS; +} +#endif + +/**************************************************************************** + * Name: lan9250_rmmac + * + * Description: + * NuttX Callback: Remove the specified MAC address from the hardware + * multicast address filtering + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be removed + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_MCASTGROUP +static int lan9250_rmmac(FAR struct net_driver_s *dev, + FAR const uint8_t *mac) +{ + /* #warning "Multicast MAC support not implemented" */ + + return -ENOSYS; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lan9250_initialize + * + * Description: + * Initialize the LAN9250 Ethernet driver. + * + * Input Parameters: + * spi - A reference to the platform's SPI driver for the LAN9250 when + * enable CONFIG_LAN9250_SPI + * qspi - A reference to the platform's SQI driver for the LAN9250 when + * enable CONFIG_LAN9250_SQI + * lower - The MCU-specific interrupt used to control low-level MCU + * functions (i.e., LAN9250 GPIO interrupts). + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +int lan9250_initialize( +#ifdef CONFIG_LAN9250_SPI + FAR struct spi_dev_s *spi, +#else + FAR struct qspi_dev_s *qspi, +#endif + FAR const struct lan9250_lower_s *lower) +{ + FAR struct lan9250_driver_s *priv = &g_lan9250; + FAR struct net_driver_s *dev = &priv->dev; + + /* Initialize the driver structure */ + + memset(priv, 0, sizeof(struct lan9250_driver_s)); + + dev->d_buf = (uint8_t *)priv->pktbuf; + dev->d_ifup = lan9250_ifup; + dev->d_ifdown = lan9250_ifdown; + dev->d_txavail = lan9250_txavail; +#ifdef CONFIG_NET_MCASTGROUP + dev->d_addmac = lan9250_addmac; + dev->d_rmmac = lan9250_rmmac; +#endif + dev->d_private = priv; + +#ifdef CONFIG_LAN9250_SPI + priv->spi = spi; +#else + priv->qspi = qspi; +#endif + + priv->lower = lower; + + /* Attach the interrupt to the driver (but don't enable it yet) */ + + if (lower->attach(lower, lan9250_interrupt, priv) < 0) + { + /* We could not attach the ISR to the interrupt */ + + return -EAGAIN; + } + + /* Try to read MAC address from low-level function. */ + + if (lower->getmac) + { + lower->getmac(lower, priv->dev.d_mac.ether.ether_addr_octet); + } + + /* Register the device with the OS so that socket IOCTLs can be performed */ + + return netdev_register(dev, NET_LL_ETHERNET); +} diff --git a/drivers/net/lan9250.h b/drivers/net/lan9250.h new file mode 100644 index 00000000000..6eff7e758ef --- /dev/null +++ b/drivers/net/lan9250.h @@ -0,0 +1,628 @@ +/**************************************************************************** + * drivers/net/lan9250.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing software + * distributed under the License is distributed on an "AS IS" BASIS WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __DRIVERS_NET_LAN9250_H +#define __DRIVERS_NET_LAN9250_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* LAN9250 SPI Commands *****************************************************/ + +#define LAN9250_SPI_CMD_READ 0x03 +#define LAN9250_SPI_CMD_FREAD 0x0b +#define LAN9250_SPI_CMD_WRITE 0x02 +#define LAN9250_SPI_CMD_ENABLE_SQI 0x38 + +#define LAN9250_QSPI_CMD_READ 0xeb +#define LAN9250_QSPI_CMD_WRITE 0xe2 + +/* System Control and Status Registers **************************************/ + +#define LAN9250_RXDFR 0x000 +#define LAN9250_TXDFR 0x020 +#define LAN9250_RXSFR 0x040 +#define LAN9250_RXSPR 0x044 +#define LAN9250_TXSFR 0x048 +#define LAN9250_TXSPR 0x04c +#define LAN9250_CIARR 0x050 +#define LAN9250_ICFGR 0x054 +#define LAN9250_ISR 0x058 +#define LAN9250_IER 0x05c +#define LAN9250_BOTR 0x064 +#define LAN9250_FLIR 0x068 +#define LAN9250_RXCFGR 0x06c +#define LAN9250_TXCFGR 0x070 +#define LAN9250_HWCFGR 0x074 +#define LAN9250_RDCR 0x078 +#define LAN9250_RXFIR 0x07c +#define LAN9250_TXFIR 0x080 +#define LAN9250_PMCR 0x084 +#define LAN9250_GPTCFGR 0x08c +#define LAN9250_GPTCR 0x090 +#define LAN9250_FR25MCR 0x09c +#define LAN9250_HMRDFCR 0x0a0 +#define LAN9250_HMCSRICR 0x0a4 +#define LAN9250_HMCSRIDR 0x0a8 +#define LAN9250_HMAFCCFGR 0x0ac +#define LAN9250_HMRXLPITXR 0x0b0 +#define LAN9250_HMRXLPITR 0x0b4 +#define LAN9250_HMTXLPITXR 0x0b8 +#define LAN9250_HMTXLPITR 0x0bc + +/* 1588 Register Addresses */ + +#define LAN9250_1588CACR 0x100 +#define LAN9250_1588GCFGR 0x104 +#define LAN9250_1588ISR 0x108 +#define LAN9250_1588IER 0x10c +#define LAN9250_1588CLKSR 0x110 +#define LAN9250_1588CLKNSR 0x114 +#define LAN9250_1588CLKSNSR 0x118 +#define LAN9250_1588CLKRAR 0x11c +#define LAN9250_1588CLKTRAR 0x120 +#define LAN9250_1588CLKTRDR 0x124 +#define LAN9250_1588CLKSAR 0x128 +#define LAN9250_1588CLKATXSR 0x12c +#define LAN9250_1588CLKATXNSR 0x130 +#define LAN9250_1588CLKATXRASR 0x134 +#define LAN9250_1588CLKATXRANSR 0x138 +#define LAN9250_1588CLKBTXSR 0x13c +#define LAN9250_1588CLKBTXNSR 0x140 +#define LAN9250_1588CLKBTXRASR 0x144 +#define LAN9250_1588CLKBTXRANSR 0x148 +#define LAN9250_1588UMAHWR 0x14c +#define LAN9250_1588UMALWR 0x150 +#define LAN9250_1588BPGSR 0x154 + +/* Bank 0 Control Register Addresses */ + +#define LAN9250_1588PLR 0x158 +#define LAN9250_1588PAAPDR 0x15c +#define LAN9250_1588PCIR 0x160 + +/* Bank 1 Control Register Addresses */ + +#define LAN9250_1588PRXPCR 0x158 +#define LAN9250_1588PRXTSCR 0x15c +#define LAN9250_1588PRXTICR 0x160 +#define LAN9250_1588PRXFCFGR 0x168 +#define LAN9250_1588PRXITSR 0x16c +#define LAN9250_1588PRXITNSR 0x170 +#define LAN9250_1588PRXMHR 0x174 +#define LAN9250_1588PRXPDRITSR 0x178 +#define LAN9250_1588PRXPDRITNSR 0x17c +#define LAN9250_1588PRXPDRICFHR 0x180 +#define LAN9250_1588PRXPDRICFLR 0x184 +#define LAN9250_1588PRXCSDCR 0x188 +#define LAN9250_1588PRXFCR 0x18c + +/* Bank 2 Control Register Addresses */ + +#define LAN9250_1588PTXPCR 0x158 +#define LAN9250_1588PTXTSCR 0x15c +#define LAN9250_1588PTXMR 0x164 +#define LAN9250_1588PTXM2R 0x168 +#define LAN9250_1588PTXETSR 0x16c +#define LAN9250_1588PTXETNSR 0x170 +#define LAN9250_1588PTXMHR 0x174 +#define LAN9250_1588PTXDRDTSR 0x178 +#define LAN9250_1588PTXDRDTNSR 0x17c +#define LAN9250_1588TXOSSUSR 0x180 + +/* Bank 3 Control Register Addresses */ + +#define LAN9250_1588GPIOCCFGR 0x15c +#define LAN9250_1588GPIOXRECLKSCR 0x16c +#define LAN9250_1588GPIOXRECLKNSCR 0x170 +#define LAN9250_1588GPIOXFECLKSCR 0x178 +#define LAN9250_1588GPIOXFECLKNSCR 0x17c + +/* ISR_GPIO Register Addresses */ + +#define LAN9250_GPIOCFGR 0x1e0 +#define LAN9250_GPIODADR 0X1e4 +#define LAN9250_GPIOISAER 0X1e8 + +/* Reset Register Addresses */ + +#define LAN9250_RSTCR 0x1f8 + +/* Host MAC Registers *******************************************************/ + +#define LAN9250_HMACCR 0x01 +#define LAN9250_HMACAHR 0x02 +#define LAN9250_HMACALR 0x03 +#define LAN9250_HMACHTHR 0x04 +#define LAN9250_HMACHTLR 0x05 +#define LAN9250_HMACMIIAR 0x06 +#define LAN9250_HMACMIIDR 0x07 +#define LAN9250_HMACFCR 0x08 +#define LAN9250_HMACVLAN1TR 0x09 +#define LAN9250_HMACVLAN2TR 0x0a +#define LAN9250_HMACWUFFR 0x0b +#define LAN9250_HMACWUCASR 0x0c +#define LAN9250_HMACCSOECR 0x0d +#define LAN9250_HMACEEETWTXSR 0x0e +#define LAN9250_HMACEEETXLPIRDR 0x0f + +/* PHY Registers ************************************************************/ + +#define LAN9250_PHYBCR 0 +#define LAN9250_PHYBSR 1 +#define LAN9250_PHYIMSBR 2 +#define LAN9250_PHYILSBR 3 +#define LAN9250_PHYANAR 4 +#define LAN9250_PHYANLPBPAR 5 +#define LAN9250_PHYANER 6 +#define LAN9250_PHYANNPTXR 7 +#define LAN9250_PHYANNPRXR 8 +#define LAN9250_PHYMMDACR 13 +#define LAN9250_PHYMMDAODR 14 +#define LAN9250_PHYEDPDCFGR 16 +#define LAN9250_PHYMCOSR 17 +#define LAN9250_PHYSMR 18 +#define LAN9250_PHYTDRPODCR 24 +#define LAN9250_PHYTDRCOSR 25 +#define LAN9250_PHYSECR 26 +#define LAN9250_PHYSCOSIR 27 +#define LAN9250_PHYCLR 28 +#define LAN9250_PHYISFR 29 +#define LAN9250_PHYIER 30 +#define LAN9250_PHYSCOSR 31 + +/* LAN9250 SPI Commands Fields **********************************************/ + +/* SPI Write Command A */ + +#define SPI_CMD_WRITE_A_IOC 0X80000000 +#define SPI_CMD_WRITE_A_BEA 0x03000000 +#define SPI_CMD_WRITE_A_4BA 0x00000000 +#define SPI_CMD_WRITE_A_16BA 0x01000000 +#define SPI_CMD_WRITE_A_32BA 0x02000000 +#define SPI_CMD_WRITE_A_DO 0x00000000 +#define SPI_CMD_WRITE_A_FS 0x00002000 +#define SPI_CMD_WRITE_A_LS 0x00001000 +#define SPI_CMD_WRITE_A_BS_M 0x000007ff + +/* SPI Write Command B */ + +#define SPI_CMD_WRITE_B_PT 0x05200000 +#define SPI_CMD_WRITE_B_CE 0x00004000 +#define SPI_CMD_WRITE_B_ACD 0x00002000 +#define SPI_CMD_WRITE_B_DP 0x00001000 +#define SPI_CMD_WRITE_B_PL_M 0x000007ff + +/* RX FIFO Status Format Fields *********************************************/ + +#define RXSFF_PF 0x80000000 +#define RXSFF_FF 0x40000000 +#define RXSFF_PL_M 0x3fff0000 +#define RXSFF_PL_S 16 +#define RXSFF_ES 0x00008000 +#define RXSFF_BF 0x00002000 +#define RXSFF_LE 0x00001000 +#define RXSFF_RF 0x00000800 +#define RXSFF_MF 0x00000400 +#define RXSFF_FTL 0x00000080 +#define RXSFF_CS 0x00000040 +#define RXSFF_FT 0x00000020 +#define RXSFF_RWTO 0x00000010 +#define RXSFF_MIIE 0x00000008 +#define RXSFF_DB 0x00000004 +#define RXSFF_CRCE 0x00000002 + +/* System Control and Status Registers Fields *******************************/ + +/* RX FIFO Status Register */ + +#define RXFIR_RXSFUS_M 0x00ff0000 +#define RXFIR_RXSFUS_S 16 +#define RXFIR_RXDFUS_M 0x0000ffff + +/* TX FIFO Status Register */ + +#define TXFIR_TXSFUS_M 0x00ff0000 +#define TXFIR_TXSFUS_S 16 +#define TXFIR_TXDFFS_M 0x0000ffff + +/* Hardware Configuration Register */ + +#define HWCFGR_READY 0x08000000 +#define HWCFGR_AMDIXENSS 0x02000000 +#define HWCFGR_MBO 0x00100000 +#define HWCFGR_TXFS_M 0x000f0000 +#define HWCFGR_TXFS_S 16 + +/* Byte Order Test Register */ + +#define BOTR_MASK 0xffffffff +#define BOTR_VAL 0x87654321 + +/* Host MAC Automatic Flow Control Configuration Register */ + +#define HMAFCCFGR_AFCHL_M 0x00ff0000 +#define HMAFCCFGR_AFCHL_S 16 +#define HMAFCCFGR_AFCLL_M 0x0000ff00 +#define HMAFCCFGR_AFCLL_S 8 +#define HMAFCCFGR_BPD_M 0x000000f0 +#define HMAFCCFGR_BPD_S 4 +#define HMAFCCFGR_FCOMF 0x00000008 +#define HMAFCCFGR_FCOBF 0x00000004 +#define HMAFCCFGR_FCOAD 0x00000002 +#define HMAFCCFGR_FCOAF 0x00000001 + +/* Chip ID and Revision Register */ + +#define CIARR_CID_M 0xffff0000 +#define CIARR_CID_S 16 +#define CIARR_CID_V (0x9250 << 16) +#define CIARR_CREV_M 0x0000ffff + +/* Interrupt Configuration Register */ + +#define ICFGR_IDAI_M 0xf0000000 +#define ICFGR_IDAI_S 24 +#define ICFGR_IDAIC 0x00004000 +#define ICFGR_IDAS 0x00002000 +#define ICFGR_MI 0x00001000 +#define ICFGR_IRQE 0x00000100 +#define ICFGR_IRQP 0x00000010 +#define ICFGR_IRQCLKS 0x00000002 +#define ICFGR_IRQBT 0x00000001 + +/* Interrupt Enable Register */ + +#define IER_SW 0x80000000 +#define IER_RAEDY 0x40000000 +#define IER_1588 0x20000000 +#define IER_PHY 0x04000000 +#define IER_TXSTOP 0x02000000 +#define IER_RXSTOP 0x01000000 +#define IER_RXDFH 0x00800000 +#define IER_TXIOC 0x00200000 +#define IER_RXD 0x00100000 +#define IER_GPT 0x00080000 +#define IER_PME 0x00020000 +#define IER_TXSO 0x00010000 +#define IER_RWT 0x00008000 +#define IER_RXE 0x00004000 +#define IER_TXE 0x00002000 +#define IER_GPIO 0x00001000 +#define IER_TDFO 0x00000400 +#define IER_TDFA 0x00000200 +#define IER_TSFF 0x00000100 +#define IER_TSFL 0x00000080 +#define IER_RXDF 0x00000040 +#define IER_RSFF 0x00000010 +#define IER_RSFL 0x00000008 + +/* Interrupt Status Register */ + +#define ISR_SW 0x80000000 +#define ISR_READY 0x40000000 +#define ISR_1588 0x20000000 +#define ISR_PHY 0x04000000 +#define ISR_TXS 0x02000000 +#define ISR_RXS 0x01000000 +#define ISR_RXDFH 0x00800000 +#define ISR_TXIOC 0x00200000 +#define ISR_RXD 0x00100000 +#define ISR_GPT 0x00080000 +#define ISR_PME 0x00020000 +#define ISR_TXSO 0x00010000 +#define ISR_RWT 0x00008000 +#define ISR_RXE 0x00004000 +#define ISR_TXE 0x00002000 +#define ISR_GPIO 0x00001000 +#define ISR_TDFO 0x00000400 +#define ISR_TDFA 0x00000200 +#define ISR_TSFF 0x00000100 +#define ISR_TSFL 0x00000080 +#define ISR_RXDF 0x00000040 +#define ISR_RSFF 0x00000010 +#define ISR_RSFL 0x00000008 + +/* FIFO Level Interrupt Register */ + +#define FLIR_FITXDAL_M 0xff000000 +#define FLIR_FITXDAL_S 24 +#define FLIR_FITXSL_M 0x00ff0000 +#define FLIR_FITXSL_S 16 +#define FLIR_FIRXSL_M 0x000000ff +#define FLIR_FIRXSL_S 0 + +/* Receive Configuration Register */ + +#define RXCFGR_RXEA 0xc0000000 +#define RXCFGR_RXDMAC 0x06000000 +#define RXCFGR_RXDMAC_M 0x0fff0000 +#define RXCFGR_RXDMAC_S 16 +#define RXCFGR_FRXD 0x00008000 +#define RXCFGR_RXDO_M 0x00001f00 +#define RXCFGR_RXDO_S 8 + +/* Transmit Configuration Register */ + +#define TXCFGR_FTXSD 0x00008000 +#define TXCFGR_FTXDD 0x00004000 +#define TXCFGR_TXSAO 0x00000004 +#define TXCFGR_TXE 0x00000002 +#define TXCFGR_TXD 0x00000001 + +/* Power Management Control Register */ + +#define PMCR_PMM_M 0xe0000000 +#define PMCR_PMSE 0x10000000 +#define PMCR_PMWU 0x08000000 +#define PMCR_LEDD 0x04000000 +#define PMCR_1588CLKD 0x02000000 +#define PMCR_1588TSUCLKD 0x00400000 +#define PMCR_HMACCLKD 0x00080000 +#define PMCR_HMACSCOD 0x00040000 +#define PMCR_EDS 0x00010000 +#define PMCR_EDE 0x00004000 +#define PMCR_WOE 0x00000200 +#define PMCR_PMEBT 0x00000040 +#define PMCR_WOLS 0x00000020 +#define PMCR_PMEI 0x00000008 +#define PMCR_PMEP 0x00000004 +#define PMCR_PMEE 0x00000002 +#define PMCR_READY 0x00000001 + +/* Host MAC CSR Interface Command Register */ + +#define HMCSRICR_CSRB 0x80000000 +#define HMCSRICR_RNW 0x40000000 +#define HMCSRICR_CSRA_M 0x000000ff + +/* Reset Control Register */ + +#define RSTCR_HMACR 0x00000020 +#define RSTCR_PHYR 0x00000002 +#define RSTCR_DR 0x00000001 + +/* Host MAC Registers Fields ************************************************/ + +/* Host MAC Control Register */ + +#define HMACCR_RXAM 0x80000000 +#define HMACCR_EEEE 0x02000000 +#define HMACCR_DRXO 0x00400000 +#define HMACCR_LOM 0x00200000 +#define HMACCR_FDM 0x00100000 +#define HMACCR_PAM 0x00080000 +#define HMACCR_PM 0x00040000 +#define HMACCR_IF 0x00020000 +#define HMACCR_PBF 0x00010000 +#define HMACCR_HOFM 0x00008000 +#define HMACCR_HOPFM 0x00002000 +#define HMACCR_DBF 0x00000800 +#define HMACCR_DR 0x00000400 +#define HMACCR_APS 0x00000100 +#define HMACCR_BOL_M 0x000000c0 +#define HMACCR_DC 0x00000020 +#define HMACCR_TXE 0x00000008 +#define HMACCR_RXE 0x00000004 + +/* Host MAC MII Access Register */ + +#define HMACMIIAR_PHYA_M 0x0000f800 +#define HMACMIIAR_PHYA_S 11 +#define HMACMIIAR_MIIRX_M 0x000007c0 +#define HMACMIIAR_MIIRX_S 6 +#define HMACMIIAR_MIIW 0x00000002 +#define HMACMIIAR_MIIB 0x00000001 + +/* Host MAC Flow Control Register */ + +#define HMACFCR_PT_M 0xffff0000 +#define HMACFCR_PT_S 16 +#define HMACFCR_PCF 0x00000004 +#define HMACFCR_FLE 0x00000002 +#define HMACFCR_FLB 0x00000001 + +/* PHY Registers Fields *****************************************************/ + +/* PHY Basic Control Register */ + +#define PHYBCR_SWR 0x8000 +#define PHYBCR_LB 0x4000 +#define PHYBCR_SSLSB 0x2000 +#define PHYBCR_ANE 0x1000 +#define PHYBCR_PD 0x0800 +#define PHYBCR_RAN 0x0200 +#define PHYBCR_DM 0x0100 +#define PHYBCR_CTM 0x0080 + +/* PHY Basic Status Register */ + +#define PHYBSR_100BT4 0x8000 +#define PHYBSR_100BXFD 0x4000 +#define PHYBSR_100BXHD 0x2000 +#define PHYBSR_10BXFD 0x1000 +#define PHYBSR_10BXHD 0x0800 +#define PHYBSR_100BT2FD 0x0400 +#define PHYBSR_100BT2HD 0x0200 +#define PHYBSR_ES 0x0100 +#define PHYBSR_UDA 0x0080 +#define PHYBSR_MFPS 0x0040 +#define PHYBSR_ANC 0x0020 +#define PHYBSR_RF 0x0010 +#define PHYBSR_ANA 0x0008 +#define PHYBSR_LS 0x0004 +#define PHYBSR_JD 0x0002 +#define PHYBSR_EC 0x0001 + +/* PHY Identification MSB Register */ + +#define PHYIMSBR_ID 0xffff + +/* PHY Identification LSB Register */ + +#define PHYILSBR_ID_M 0xfc00 +#define PHYILSBR_ID_S 10 +#define PHYILSBR_MN_M 0x03f0 +#define PHYILSBR_MN_S 4 +#define PHYILSBR_RN_M 0x000f +#define PHYILSBR_RN_S 0 + +/* PHY Auto-Negotiation Advertisement Register */ + +#define PHYANAR_NP 0x8000 +#define PHYANAR_RF 0x2000 +#define PHYANAR_ENP 0x1000 +#define PHYANAR_AP 0x0800 +#define PHYANAR_SP 0x0400 +#define PHYANAR_100BXFD 0x0100 +#define PHYANAR_100BXHD 0x0080 +#define PHYANAR_10BXFD 0x0040 +#define PHYANAR_10BXHD 0x0020 +#define PHYANAR_SF 0x0001 + +/* PHY Auto-Negotiation Link Partner Base Page Ability Register */ + +#define PHYANLPBPAR_NP 0x8000 +#define PHYANLPBPAR_ACK 0x4000 +#define PHYANLPBPAR_RF 0x2000 +#define PHYANLPBPAR_ENP 0x1000 +#define PHYANLPBPAR_AP 0x0800 +#define PHYANLPBPAR_PAUSE 0x0400 +#define PHYANLPBPAR_100BT4 0x0200 +#define PHYANLPBPAR_100BXFD 0x0100 +#define PHYANLPBPAR_100BXHD 0x0080 +#define PHYANLPBPAR_10BXFD 0x0040 +#define PHYANLPBPAR_10BXHD 0x0020 +#define PHYANLPBPAR_SF_M 0x001f + +/* PHY Auto-Negotiation Expansion Register */ + +#define PHYANER_RXNPLA 0x0040 +#define PHYANER_RXNPSL 0x0020 +#define PHYANER_PDF 0x0010 +#define PHYANER_LPNPA 0x0008 +#define PHYANER_NPA 0x0004 +#define PHYANER_PRX 0x0002 +#define PHYANER_LPANA 0x0001 + +/* PHY Auto-Negotiation Next Page TX Register */ + +#define PHYANNPTXR_NP 0x8000 +#define PHYANNPTXR_MP 0x2000 +#define PHYANNPTXR_ACK2 0x1000 +#define PHYANNPTXR_TOG 0x0800 +#define PHYANNPTXR_MC_M 0x07ff + +/* PHY Auto-Negotiation Next Page RX Register */ + +#define PHYANNPRXR_NP 0x8000 +#define PHYANNPRXR_ACK 0x4000 +#define PHYANNPRXR_MP 0x2000 +#define PHYANNPRXR_ACK2 0x1000 +#define PHYANNPRXR_TOG 0x0800 +#define PHYANNPRXR_MC_M 0x07ff + +/* PHY MMD Access Control Register */ + +#define PHYMMDACR_MMDF_M 0xc000 +#define PHYMMDACR_MMDDA_M 0x001f + +/* PHY EDPD NLP/Crossover Time/EEE Configuration Register */ + +#define PHYEDPDCFGR_EDPDTXNLPE 0x8000 +#define PHYEDPDCFGR_EDPDTXNLPITS_M 0x6000 +#define PHYEDPDCFGR_EDPDRXSNLPWE 0x1000 +#define PHYEDPDCFGR_EDPDRXNLPMIDS_M 0x0c00 +#define PHYEDPDCFGR_PEEEE 0x0004 +#define PHYEDPDCFGR_EDPDEC 0x0002 +#define PHYEDPDCFGR_EMAMDIXCT 0x0001 + +/* PHY Mode Control/Status Register */ + +#define PHYMCOSR_EDPD 0x2000 +#define PHYMCOSR_AIM 0x0040 +#define PHYMCOSR_EO 0x0002 + +/* PHY Special Modes Register */ + +#define PHYSMR_100BFXM 0x0400 +#define PHYSMR_PM_M 0x00e0 +#define PHYSMR_PM_S 5 +#define PHYSMR_ADDR_M 0x001f +#define PHYSMR_ADDR_S 0x00e0 + +/* PHY TDR Patterns/Delay Control Register */ + +#define PHYTDRCOSR_TDRDI 0x8000 +#define PHYTDRCOSR_TDRLBC_M 0x7000 +#define PHYTDRCOSR_TDRPH 0x0fc0 +#define PHYTDRCOSR_TDRPL 0x003f + +/* PHY TDR Control/Status Register */ + +#define PHYTDRCOSR_TDRE 0x8000 +#define PHYTDRCOSR_TDRATDFE 0x4000 +#define PHYTDRCOSR_TDRCCT 0x0600 +#define PHYTDRCOSR_TDRCS 0x0100 +#define PHYTDRCOSR_TDRCL 0x00FF + +/* PHY Special Control/Status Indication register */ + +#define PHYSCOSIR_AMDIXC 0x8000 +#define PHYSCOSIR_AMDIXE 0x4000 +#define PHYSCOSIR_DMDIXS 0x2000 +#define PHYSCOSIR_SQETD 0x0800 +#define PHYSCOSIR_FEFIE 0x0020 +#define PHYSCOSIR_10BTPS 0x0010 + +/* PHY Cable Length Register */ + +#define PHYCLR_CL_M 0xf000 + +/* PHY Interrupt Source Flags Register */ + +#define PHYISFR_LU 0x0200 +#define PHYISFR_EO 0x0080 +#define PHYISFR_ANC 0x0040 +#define PHYISFR_RF 0x0020 +#define PHYISFR_LD 0x0010 +#define PHYISFR_ANLPA 0x0008 +#define PHYISFR_PDF 0x0004 +#define PHYISFR_ANPR 0x0002 + +/* PHY Interrupt Enable Register */ + +#define PHYIER_LU 0x0200 +#define PHYIER_EO 0x0080 +#define PHYIER_ANC 0x0040 +#define PHYIER_RF 0x0020 +#define PHYIER_LD 0x0010 +#define PHYIER_ANLPA 0x0008 +#define PHYIER_PDF 0x0004 +#define PHYIER_ANPR 0x0002 + +/* PHY Special Control/Status Register */ + +#define PHYSCOSR_AD 0x8000 +#define PHYSCOSR_RD 0x0040 +#define PHYSCOSR_SI_M 0x001c + +#endif /* __DRIVERS_NET_LAN9250_H */ diff --git a/include/nuttx/net/lan9250.h b/include/nuttx/net/lan9250.h new file mode 100644 index 00000000000..57022121023 --- /dev/null +++ b/include/nuttx/net/lan9250.h @@ -0,0 +1,125 @@ +/**************************************************************************** + * include/nuttx/net/lan9250.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_NET_LAN9250_H +#define __INCLUDE_NUTTX_NET_LAN9250_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* The LAN9250 device typically provides interrupts to the MCU through a GPIO + * pin. The structure below provides an MCU-independent mechanism for + * controlling this LAN9250 GPIO interrupt. + * + * "When an enabled interrupt occurs, the interrupt pin will remain low until + * all flags which are causing the interrupt are cleared or masked off + * (enable bit is cleared) by the host controller." However, the interrupt + * will behave like a falling edge interrupt because "After an interrupt + * occurs, the host controller [clears] the global enable bit for the + * interrupt pin before servicing the interrupt. Clearing the enable bit + * will cause the interrupt pin to return to the non-asserted state (high). + * Doing so will prevent the host controller from missing a falling edge + * should another interrupt occur while the immediate interrupt is being + * serviced." + */ + +struct lan9250_lower_s +{ + int (*attach)(FAR const struct lan9250_lower_s *lower, xcpt_t handler, + FAR void *arg); + void (*enable)(FAR const struct lan9250_lower_s *lower); + void (*disable)(FAR const struct lan9250_lower_s *lower); + + /* This function is optional and used to get a specific MAC address from + * a MCU-specific implementation. If this function is NULL, the LAN9250 + * driver will not read the MAC from the CPU. + */ + + void (*getmac)(FAR const struct lan9250_lower_s *lower, FAR uint8_t *mac); +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: lan9250_initialize + * + * Description: + * Initialize the LAN9250 Ethernet driver. + * + * Input Parameters: + * spi - A reference to the platform's SPI driver for the LAN9250 when + * enable CONFIG_LAN9250_SPI + * qspi - A reference to the platform's SQI driver for the LAN9250 when + * enable CONFIG_LAN9250_SQI + * lower - The MCU-specific interrupt used to control low-level MCU + * functions (i.e., LAN9250 GPIO interrupts). + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_LAN9250_SPI +struct spi_dev_s; /* see nuttx/spi/spi.h */ +#else +struct qspi_dev_s; /* see nuttx/spi/qspi.h */ +#endif +int lan9250_initialize( +#ifdef CONFIG_LAN9250_SPI + FAR struct spi_dev_s *spi, +#else + FAR struct qspi_dev_s *qspi, +#endif + FAR const struct lan9250_lower_s *lower); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_NET_LAN9250_H */