You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by pk...@apache.org on 2022/06/21 09:06:45 UTC
[incubator-nuttx] 01/04: arch/risc-v/src/litex/litex_emac: add liteeth peripheral driver
This is an automated email from the ASF dual-hosted git repository.
pkarashchenko pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit 8f36649aadd6db2a89df5f6ad3feca4cbc72d42f
Author: Richard Tucker <ri...@motec.com.au>
AuthorDate: Mon May 30 11:43:34 2022 +1000
arch/risc-v/src/litex/litex_emac: add liteeth peripheral driver
See the following for details on the pepheral:
https://github.com/enjoy-digital/liteeth
---
arch/risc-v/include/litex/irq.h | 3 +-
arch/risc-v/src/litex/Kconfig | 20 +
arch/risc-v/src/litex/Make.defs | 4 +
arch/risc-v/src/litex/hardware/litex_emac.h | 87 ++
arch/risc-v/src/litex/hardware/litex_memorymap.h | 19 +-
arch/risc-v/src/litex/litex_emac.c | 1555 ++++++++++++++++++++++
6 files changed, 1680 insertions(+), 8 deletions(-)
diff --git a/arch/risc-v/include/litex/irq.h b/arch/risc-v/include/litex/irq.h
index 7056877776..24b8d53760 100644
--- a/arch/risc-v/include/litex/irq.h
+++ b/arch/risc-v/include/litex/irq.h
@@ -35,7 +35,8 @@
#define LITEX_IRQ_UART0 (RISCV_IRQ_MEXT + 1)
#define LITEX_IRQ_TIMER0 (RISCV_IRQ_MEXT + 2)
-#define LITEX_IRQ_SDCARD (RISCV_IRQ_MEXT + 3)
+#define LITEX_IRQ_ETHMAC (RISCV_IRQ_MEXT + 3)
+#define LITEX_IRQ_SDCARD (RISCV_IRQ_MEXT + 4)
/* Total number of IRQs */
diff --git a/arch/risc-v/src/litex/Kconfig b/arch/risc-v/src/litex/Kconfig
index b678910bea..1f20f37372 100644
--- a/arch/risc-v/src/litex/Kconfig
+++ b/arch/risc-v/src/litex/Kconfig
@@ -62,4 +62,24 @@ config LITEX_SD4BIT_FREQ
Frequency to use for transferring data to/from an SD card using all four data lines.
endif
+
+config LITEX_ETHMAC
+ bool "ETHMAC"
+ default n
+ select ARCH_HAVE_PHY
+ select ARCH_HAVE_NETDEV_STATISTICS
+ select NET
+ select NETDEVICES
+
endmenu
+
+menu "LITEX EMAC device driver options"
+ depends on LITEX_ETHMAC
+
+config LITEX_EMAC_PHYADDR
+ int "PHY address"
+ default 1
+ ---help---
+ The 5-bit address of the PHY on the board. Default: 1
+
+endmenu # PHY interface
diff --git a/arch/risc-v/src/litex/Make.defs b/arch/risc-v/src/litex/Make.defs
index 450961a8c9..5827658d93 100644
--- a/arch/risc-v/src/litex/Make.defs
+++ b/arch/risc-v/src/litex/Make.defs
@@ -34,3 +34,7 @@ CHIP_ASRCS += litex_cache.S
ifeq ($(CONFIG_LITEX_SDIO),y)
CHIP_CSRCS += litex_sdio.c
endif
+
+ifeq ($(CONFIG_LITEX_ETHMAC),y)
+CHIP_CSRCS += litex_emac.c
+endif
diff --git a/arch/risc-v/src/litex/hardware/litex_emac.h b/arch/risc-v/src/litex/hardware/litex_emac.h
new file mode 100644
index 0000000000..10643c56e2
--- /dev/null
+++ b/arch/risc-v/src/litex/hardware/litex_emac.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/hardware/litex_emac.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_LITEX_HARDWARE_LITEX_EMAC_H
+#define __ARCH_RISCV_SRC_LITEX_HARDWARE_LITEX_EMAC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/litex_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ETHMAC_RX_SLOTS 2
+#define ETHMAC_TX_SLOTS 2
+#define ETHMAC_SLOT_SIZE 2048
+
+/* EMAC Register Offsets ****************************************************/
+
+#define LITEX_ETHMAC_SRAM_WRITER_SLOT_OFFSET 0x0000
+#define LITEX_ETHMAC_SRAM_WRITER_LENGTH_OFFSET 0x0004
+#define LITEX_ETHMAC_SRAM_WRITER_ERRORS_OFFSET 0x0008
+#define LITEX_ETHMAC_SRAM_WRITER_EV_STATUS_OFFSET 0x000c
+#define LITEX_ETHMAC_SRAM_WRITER_EV_PENDING_OFFSET 0x0010
+#define LITEX_ETHMAC_SRAM_WRITER_EV_ENABLE_OFFSET 0x0014
+#define LITEX_ETHMAC_SRAM_READER_START_OFFSET 0x0018
+#define LITEX_ETHMAC_SRAM_READER_READY_OFFSET 0x001c
+#define LITEX_ETHMAC_SRAM_READER_LEVEL_OFFSET 0x0020
+#define LITEX_ETHMAC_SRAM_READER_SLOT_OFFSET 0x0024
+#define LITEX_ETHMAC_SRAM_READER_LENGTH_OFFSET 0x0028
+#define LITEX_ETHMAC_SRAM_READER_EV_STATUS_OFFSET 0x002c
+#define LITEX_ETHMAC_SRAM_READER_EV_PENDING_OFFSET 0x0030
+#define LITEX_ETHMAC_SRAM_READER_EV_ENABLE_OFFSET 0x0034
+#define LITEX_ETHMAC_PREAMBLE_CRC_OFFSET 0x0038
+#define LITEX_ETHMAC_RX_DATAPATH_PREAMBLE_ERRORS_OFFSET 0x003c
+#define LITEX_ETHMAC_RX_DATAPATH_CRC_ERRORS_OFFSET 0x0040
+
+#define LITEX_ETHPHY_CRG_RESET_OFFSET 0x0000
+#define LITEX_ETHPHY_MDIO_W_OFFSET 0x0004
+#define LITEX_ETHPHY_MDIO_R_OFFSET 0x0008
+
+/* LITEX_EMAC register addresses ********************************************/
+
+#define LITEX_ETHMAC_SRAM_WRITER_SLOT (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_WRITER_SLOT_OFFSET)
+#define LITEX_ETHMAC_SRAM_WRITER_LENGTH (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_WRITER_LENGTH_OFFSET)
+#define LITEX_ETHMAC_SRAM_WRITER_ERRORS (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_WRITER_ERRORS_OFFSET)
+#define LITEX_ETHMAC_SRAM_WRITER_EV_STATUS (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_WRITER_EV_STATUS_OFFSET)
+#define LITEX_ETHMAC_SRAM_WRITER_EV_PENDING (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_WRITER_EV_PENDING_OFFSET)
+#define LITEX_ETHMAC_SRAM_WRITER_EV_ENABLE (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_WRITER_EV_ENABLE_OFFSET)
+#define LITEX_ETHMAC_SRAM_READER_START (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_READER_START_OFFSET)
+#define LITEX_ETHMAC_SRAM_READER_READY (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_READER_READY_OFFSET)
+#define LITEX_ETHMAC_SRAM_READER_LEVEL (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_READER_LEVEL_OFFSET)
+#define LITEX_ETHMAC_SRAM_READER_SLOT (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_READER_SLOT_OFFSET)
+#define LITEX_ETHMAC_SRAM_READER_LENGTH (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_READER_LENGTH_OFFSET)
+#define LITEX_ETHMAC_SRAM_READER_EV_STATUS (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_READER_EV_STATUS_OFFSET)
+#define LITEX_ETHMAC_SRAM_READER_EV_PENDING (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_READER_EV_PENDING_OFFSET)
+#define LITEX_ETHMAC_SRAM_READER_EV_ENABLE (LITEX_ETHMAC_BASE + LITEX_ETHMAC_SRAM_READER_EV_ENABLE_OFFSET)
+#define LITEX_ETHMAC_PREAMBLE_CRC (LITEX_ETHMAC_BASE + LITEX_ETHMAC_PREAMBLE_CRC_OFFSET)
+#define LITEX_ETHMAC_RX_DATAPATH_PREAMBLE_ERRORS (LITEX_ETHMAC_BASE + LITEX_ETHMAC_RX_DATAPATH_PREAMBLE_ERRORS_OFFSET)
+#define LITEX_ETHMAC_RX_DATAPATH_CRC_ERRORS (LITEX_ETHMAC_BASE + LITEX_ETHMAC_RX_DATAPATH_CRC_ERRORS_OFFSET)
+
+#define LITEX_ETHPHY_CRG_RESET (LITEX_ETHPHY_BASE + LITEX_ETHPHY_CRG_RESET_OFFSET)
+#define LITEX_ETHPHY_MDIO_W (LITEX_ETHPHY_BASE + LITEX_ETHPHY_MDIO_W_OFFSET)
+#define LITEX_ETHPHY_MDIO_R (LITEX_ETHPHY_BASE + LITEX_ETHPHY_MDIO_R_OFFSET)
+
+#endif /* __ARCH_RISCV_SRC_LITEX_HARDWARE_LITEX_EMAC_H */
diff --git a/arch/risc-v/src/litex/hardware/litex_memorymap.h b/arch/risc-v/src/litex/hardware/litex_memorymap.h
index 0587fa429e..5211c5464b 100644
--- a/arch/risc-v/src/litex/hardware/litex_memorymap.h
+++ b/arch/risc-v/src/litex/hardware/litex_memorymap.h
@@ -32,12 +32,17 @@
*/
#define LITEX_CPUTIMER_BASE 0xf0000800
-#define LITEX_SDBLOCK2MEM_BASE 0xf0002000
-#define LITEX_SDCORE_BASE 0xf0002800
-#define LITEX_SDIRQ_BASE 0xf0003000
-#define LITEX_SDMEM2BLOCK_BASE 0xf0003800
-#define LITEX_SDPHY_BASE 0xf0004000
-#define LITEX_TIMER0_BASE 0xf0005000
-#define LITEX_UART0_BASE 0xf0005800
+#define LITEX_ETHMAC_BASE 0xf0001000
+#define LITEX_ETHPHY_BASE 0xf0001800
+#define LITEX_SDBLOCK2MEM_BASE 0xf0003000
+#define LITEX_SDCORE_BASE 0xf0003800
+#define LITEX_SDIRQ_BASE 0xf0004000
+#define LITEX_SDMEM2BLOCK_BASE 0xf0004800
+#define LITEX_SDPHY_BASE 0xf0005000
+#define LITEX_TIMER0_BASE 0xf0006000
+#define LITEX_UART0_BASE 0xf0006800
+
+#define LITEX_ETHMAC_RXBASE 0x80000000
+#define LITEX_ETHMAC_TXBASE 0x80001000
#endif /* __ARCH_RISCV_SRC_LITEX_HARDWARE_LITEX_MEMORYMAP_H */
diff --git a/arch/risc-v/src/litex/litex_emac.c b/arch/risc-v/src/litex/litex_emac.c
new file mode 100644
index 0000000000..1aef87ab47
--- /dev/null
+++ b/arch/risc-v/src/litex/litex_emac.c
@@ -0,0 +1,1555 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_emac.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <debug.h>
+
+#include <arpa/inet.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/mii.h>
+#include <nuttx/net/arp.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/ioctl.h>
+
+#ifdef CONFIG_NET_PKT
+# include <nuttx/net/pkt.h>
+#endif
+
+#include "riscv_internal.h"
+#include "chip.h"
+#include "hardware/litex_emac.h"
+
+#ifdef CONFIG_LITEX_ETHMAC
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* If processing is not done at the interrupt level, then work queue support
+ * is required.
+ */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+# error Work queue support is required
+#else
+
+ /* Select work queue. Always use the LP work queue if available. If not,
+ * then LPWORK will re-direct to the HP work queue.
+ *
+ * NOTE: However, the network should NEVER run on the high priority work
+ * queue! That queue is intended only to service short back end interrupt
+ * processing that never suspends. Suspending the high priority work queue
+ * may bring the system to its knees!
+ */
+
+# define ETHWORK LPWORK
+#endif
+
+/* TX timeout - in seconds */
+
+#define LITEX_TXTIMEOUT (60 * CLK_TCK)
+
+/* Wait for Ethernet link Timeout - in ms */
+
+#define LITEX_WAITLINKTIMEOUT (5000)
+
+/* PHY Reset Timeout - in ms */
+
+#define LITEX_PHY_RESETTIMEOUT (20)
+
+/* LITEX MDIO register bit definitions */
+
+#define LITEX_PHY_MDIO_CK 0x01
+#define LITEX_PHY_MDIO_OE 0x02
+#define LITEX_PHY_MDIO_DO 0x04
+
+#define LITEX_PHY_MDIO_DI 0x01
+
+#define LITEX_PHY_MDIO_PREAMBLE 0xffffffff
+#define LITEX_PHY_MDIO_START 0x1
+#define LITEX_PHY_MDIO_READ 0x2
+#define LITEX_PHY_MDIO_WRITE 0x1
+#define LITEX_PHY_MDIO_TURNAROUND 0x2
+
+/* PHY definitions */
+
+#if defined(CONFIG_ETH0_PHY_DP83848C)
+# define BOARD_PHY_NAME "DP83848C"
+# define BOARD_PHYID1 MII_PHYID1_DP83848C
+# define BOARD_PHYID2 MII_PHYID2_DP83848C
+# define BOARD_PHY_STATUS MII_DP83848C_STS
+# define BOARD_PHY_10BASET(s) (((s) & MII_DP83848C_PHYSTS_SPEED) != 0)
+# define BOARD_PHY_100BASET(s) (((s) & MII_DP83848C_PHYSTS_SPEED) == 0)
+# define BOARD_PHY_ISDUPLEX(s) (((s) & MII_DP83848C_PHYSTS_DUPLEX) != 0)
+#else
+# error EMAC PHY unrecognized
+#endif
+
+#ifdef CONFIG_NET_DUMPPACKET
+# define litex_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
+#else
+# define litex_dumppacket(m,a,n)
+#endif
+
+/* Helpers ******************************************************************/
+
+/* This is a helper pointer for accessing the contents of the Ethernet
+ * header
+ */
+
+#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The litex_emac_s encapsulates all state information for EMAC peripheral */
+
+struct litex_emac_s
+{
+ bool ifup; /* true:ifup false:ifdown */
+ struct wdog_s txpoll; /* TX poll timer */
+ struct wdog_s txtimeout; /* TX timeout timer */
+ struct work_s irqwork; /* For deferring interrupt work to the work queue */
+ struct work_s pollwork; /* For deferring poll work to the work queue */
+
+ /* This holds the information visible to the NuttX network */
+
+ struct net_driver_s dev; /* Interface understood by the network */
+ uint8_t phyaddr; /* PHY address (pre-defined by pins on reset) */
+
+ uint8_t txslot;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* The driver state singleton */
+
+static struct litex_emac_s g_emac;
+
+/* Global Rx/Tx Buffer */
+
+static uint8_t g_buffer[ETHMAC_SLOT_SIZE]
+ aligned_data(8);
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void litex_txavail_work(void *arg);
+static int litex_txavail(struct net_driver_s *dev);
+static int litex_txpoll(struct net_driver_s *dev);
+static void litex_txdone(struct litex_emac_s *priv);
+static void litex_txtimeout_work(void *arg);
+static void litex_txtimeout_expiry(wdparm_t arg);
+static int litex_transmit(struct litex_emac_s *priv);
+
+static void litex_receive(struct litex_emac_s *priv);
+
+static void litex_emac_interrupt_work(void *arg);
+static int litex_emac_interrupt(int irq, void *context, void *arg);
+
+static int litex_linkup(struct litex_emac_s *priv);
+static int litex_ifup(struct net_driver_s *dev);
+static int litex_ifdown(struct net_driver_s *dev);
+
+static void litex_phywriteraw(uint32_t word, uint8_t bitcount);
+static uint16_t litex_phyreadraw(void);
+static void litex_phyturnaround(void);
+static int litex_phyread(struct litex_emac_s *priv, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t *phyval);
+#ifdef CONFIG_NETDEV_IOCTL
+static int litex_phywrite(struct litex_emac_s *priv, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t phyval);
+#endif
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void litex_phydump(struct litex_emac_s *priv);
+#else
+# define litex_phydump(priv)
+#endif
+static int litex_phyfind(struct litex_emac_s *priv, uint8_t phyaddr);
+static int litex_phyinit(struct litex_emac_s *priv);
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int litex_ioctl(struct net_driver_s *dev, int cmd,
+ unsigned long arg);
+#endif
+
+static int litex_emac_configure(struct litex_emac_s *priv);
+static int litex_buffer_init(struct litex_emac_s *priv);
+static void litex_ethinitialize(void);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: litex_txavail_work
+ *
+ * Description:
+ * Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ * arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void litex_txavail_work(void *arg)
+{
+ struct litex_emac_s *priv = (struct litex_emac_s *)arg;
+
+ /* Ignore the notification if the interface is not yet up */
+
+ net_lock();
+
+ if (priv->ifup)
+ {
+ devif_poll(&priv->dev, litex_txpoll);
+ }
+
+ net_unlock();
+}
+
+/****************************************************************************
+ * Function: litex_txavail
+ *
+ * Description:
+ * Driver callback invoked when new TX data is available. This is a
+ * stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ * latency.
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int litex_txavail(struct net_driver_s *dev)
+{
+ struct litex_emac_s *priv = (struct litex_emac_s *)dev->d_private;
+
+ /* Is our single work structure available? It may not be if there are
+ * pending interrupt actions and we will have to ignore the Tx
+ * availability action.
+ */
+
+ if (work_available(&priv->pollwork))
+ {
+ /* Schedule to serialize the poll on the worker thread. */
+
+ work_queue(ETHWORK, &priv->pollwork, litex_txavail_work, priv, 0);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: litex_txpoll
+ *
+ * Description:
+ * The transmitter is available, check if the network has any outgoing
+ * packets ready to send. This is a callback from devif_poll().
+ * devif_poll() may be called:
+ *
+ * 1. When the preceding TX packet send is complete,
+ * 2. When the preceding TX packet send timesout and the interface is reset
+ * 3. During normal TX polling
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int litex_txpoll(struct net_driver_s *dev)
+{
+ struct litex_emac_s *priv = (struct litex_emac_s *)dev->d_private;
+
+ DEBUGASSERT(priv->dev.d_buf != NULL);
+
+ /* If the polling resulted in data that should be sent out on the network,
+ * the field d_len is set to a value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ /* Look up the destination MAC address and add it to the Ethernet
+ * header.
+ */
+
+#ifdef CONFIG_NET_IPv4
+#ifdef CONFIG_NET_IPv6
+ if (IFF_IS_IPv4(priv->dev.d_flags))
+#endif
+ {
+ arp_out(&priv->dev);
+ }
+#endif /* CONFIG_NET_IPv4 */
+
+#ifdef CONFIG_NET_IPv6
+#ifdef CONFIG_NET_IPv4
+ else
+#endif
+ {
+ neighbor_out(&priv->dev);
+ }
+#endif /* CONFIG_NET_IPv6 */
+
+ if (!devif_loopback(&priv->dev))
+ {
+ /* Send the packet */
+
+ litex_transmit(priv);
+ }
+ }
+
+ /* If zero is returned, the polling will continue until all connections
+ * have been examined.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Function: litex_txdone
+ *
+ * Description:
+ * An interrupt was received indicating that the last TX packet(s) is done
+ *
+ * Input Parameters:
+ * priv - Reference to the private driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static void litex_txdone(struct litex_emac_s *priv)
+{
+ /* We are here because a transmission completed, so the watchdog can be
+ * canceled.
+ */
+
+ wd_cancel(&priv->txtimeout);
+
+ /* Update statistics */
+
+ NETDEV_TXDONE(&priv->dev);
+
+ /* There should be space for a new TX in any event. Poll the network for
+ * new XMIT data
+ */
+
+ devif_poll(&priv->dev, litex_txpoll);
+}
+
+/****************************************************************************
+ * Function: litex_txtimeout_work
+ *
+ * Description:
+ * Perform TX timeout related work from the worker thread
+ *
+ * Input Parameters:
+ * arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void litex_txtimeout_work(void *arg)
+{
+ struct litex_emac_s *priv = (struct litex_emac_s *)arg;
+
+ /* Increment statistics and dump debug info */
+
+ net_lock();
+
+ nerr("ERROR: Timeout!\n");
+ nerr("Resetting interface\n");
+
+ NETDEV_TXTIMEOUTS(&priv->dev);
+
+ /* Take the interface down and bring it back up. That is the most
+ * aggressive hardware reset.
+ */
+
+ litex_ifdown(&priv->dev);
+ litex_ifup(&priv->dev);
+
+ /* Then poll the network for new XMIT data */
+
+ devif_poll(&priv->dev, litex_txpoll);
+ net_unlock();
+}
+
+/****************************************************************************
+ * Function: litex_txtimeout_expiry
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
+ *
+ * Input Parameters:
+ * arg - The argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void litex_txtimeout_expiry(wdparm_t arg)
+{
+ struct litex_emac_s *priv = (struct litex_emac_s *)arg;
+
+ /* Disable further Ethernet interrupts. This will prevent some race
+ * conditions with interrupt work. There is still a potential race
+ * condition with interrupt work that is already queued and in progress.
+ */
+
+ up_disable_irq(LITEX_IRQ_ETHMAC);
+
+ /* Schedule to perform the TX timeout processing on the worker thread,
+ * canceling any pending interrupt work.
+ */
+
+ work_queue(ETHWORK, &priv->irqwork, litex_txtimeout_work, priv, 0);
+}
+
+/****************************************************************************
+ * Function: litex_transmit
+ *
+ * Description:
+ * Start hardware transmission. Called either from the txdone interrupt
+ * handling or from watchdog based polling.
+ *
+ * Input Parameters:
+ * priv - Reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ * May or may not be called from an interrupt handler. In either case,
+ * global interrupts are disabled, either explicitly or indirectly through
+ * interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int litex_transmit(struct litex_emac_s *priv)
+{
+ irqstate_t flags;
+
+ if (getreg32(LITEX_ETHMAC_SRAM_READER_READY) == 0)
+ {
+ nerr("Tx Busy\n");
+ return -EBUSY;
+ }
+
+ ninfo("Sending packet, length: %d slot: %d\n",
+ priv->dev.d_len, priv->txslot);
+ litex_dumppacket("Tx Packet", g_buffer, priv->dev.d_len);
+
+ /* Increment statistics */
+
+ NETDEV_TXPACKETS(&priv->dev);
+
+ /* Copy frame to the hardware buffer */
+
+ memcpy((void *)(LITEX_ETHMAC_TXBASE + (priv->txslot * ETHMAC_SLOT_SIZE)),
+ g_buffer, priv->dev.d_len);
+
+ /* Make the following operations atomic */
+
+ flags = spin_lock_irqsave(NULL);
+
+ /* Now start transmission */
+
+ putreg8(priv->txslot, LITEX_ETHMAC_SRAM_READER_SLOT);
+ putreg16(priv->dev.d_len, LITEX_ETHMAC_SRAM_READER_LENGTH);
+ putreg8(0x01, LITEX_ETHMAC_SRAM_READER_START);
+
+ /* Increment the hardware TX slot -> wrap around */
+
+ priv->txslot = (priv->txslot + 1) % ETHMAC_TX_SLOTS;
+
+ /* Setup the TX timeout watchdog */
+
+ wd_start(&priv->txtimeout, LITEX_TXTIMEOUT,
+ litex_txtimeout_expiry, (wdparm_t)priv);
+
+ spin_unlock_irqrestore(NULL, flags);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: litex_receive
+ *
+ * Description:
+ * An interrupt was received indicating the availability of one or more
+ * new RX packets in FIFO memory.
+ *
+ * Input Parameters:
+ * priv - Reference to the private driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void litex_receive(struct litex_emac_s *priv)
+{
+ /* Update statistics */
+
+ NETDEV_RXPACKETS(&priv->dev);
+
+ priv->dev.d_len = getreg16(LITEX_ETHMAC_SRAM_WRITER_LENGTH);
+
+ if (priv->dev.d_len == 0 || priv->dev.d_len > ETHMAC_SLOT_SIZE)
+ {
+ NETDEV_RXDROPPED(&priv->dev);
+ return;
+ }
+
+ /* Copy frame from hardware buffer at the slot which activated
+ * this reception
+ */
+
+ const uint8_t rxslot = getreg8(LITEX_ETHMAC_SRAM_WRITER_SLOT);
+ memcpy(g_buffer,
+ (void *)(LITEX_ETHMAC_RXBASE + (rxslot * ETHMAC_SLOT_SIZE)),
+ priv->dev.d_len);
+ litex_dumppacket("Rx Packet", g_buffer, priv->dev.d_len);
+
+#ifdef CONFIG_NET_PKT
+ /* When packet sockets are enabled, feed the frame into the tap */
+
+ pkt_input(&priv->dev);
+#endif
+
+#ifdef CONFIG_NET_IPv4
+ /* Check for an IPv4 packet */
+
+ if (BUF->type == HTONS(ETHTYPE_IP))
+ {
+ ninfo("IPv4 frame\n");
+ NETDEV_RXIPV4(&priv->dev);
+
+ /* Handle ARP on input then give the IPv4 packet to the network
+ * layer
+ */
+
+ arp_ipin(&priv->dev);
+ ipv4_input(&priv->dev);
+
+ /* If the above function invocation resulted in data that should be
+ * sent out on the network, d_len field will set to a value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ /* Update the Ethernet header with the correct MAC address */
+
+#ifdef CONFIG_NET_IPv6
+ if (IFF_IS_IPv4(priv->dev.d_flags))
+#endif
+ {
+ arp_out(&priv->dev);
+ }
+#ifdef CONFIG_NET_IPv6
+ else
+ {
+ neighbor_out(&priv->dev);
+ }
+#endif
+
+ /* And send the packet */
+
+ litex_transmit(priv);
+ }
+ }
+ else
+#endif
+#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)
+ {
+ /* Update the Ethernet header with the correct MAC address */
+
+#ifdef CONFIG_NET_IPv4
+ if (IFF_IS_IPv4(priv->dev.d_flags))
+ {
+ arp_out(&priv->dev);
+ }
+ else
+#endif
+#ifdef CONFIG_NET_IPv6
+ {
+ neighbor_out(&priv->dev);
+ }
+#endif
+
+ /* And send the packet */
+
+ litex_transmit(priv);
+ }
+ }
+ else
+#endif
+#ifdef CONFIG_NET_ARP
+ /* Check for an ARP packet */
+
+ if (BUF->type == HTONS(ETHTYPE_ARP))
+ {
+ ninfo("ARP frame\n");
+ NETDEV_RXARP(&priv->dev);
+ arp_arpin(&priv->dev);
+
+ /* If the above function invocation resulted in data that should
+ * be sent out on the network, the field d_len will set to a
+ * value > 0.
+ */
+
+ if (priv->dev.d_len > 0)
+ {
+ litex_transmit(priv);
+ }
+ }
+#endif
+ else
+ {
+ NETDEV_RXDROPPED(&priv->dev);
+ }
+}
+
+/****************************************************************************
+ * Function: litex_emac_interrupt_work
+ *
+ * Description:
+ * Perform interrupt related work from the worker thread
+ *
+ * Input Parameters:
+ * arg - The argument passed when work_queue() was called.
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ * Runs on a worker thread.
+ * The network is locked.
+ *
+ ****************************************************************************/
+
+static void litex_emac_interrupt_work(void *arg)
+{
+ struct litex_emac_s *priv = (struct litex_emac_s *)arg;
+
+ /* Process pending Ethernet interrupts */
+
+ net_lock();
+
+ /* Rx Available */
+
+ if (getreg32(LITEX_ETHMAC_SRAM_WRITER_EV_PENDING) & 0x01)
+ {
+ litex_receive(priv);
+
+ /* ACK the pending flag AFTER receiving and processing data.
+ * This transitions the receive slot in hardware.
+ */
+
+ putreg8(0x01, LITEX_ETHMAC_SRAM_WRITER_EV_PENDING);
+ }
+
+ /* Tx Done */
+
+ if (getreg32(LITEX_ETHMAC_SRAM_READER_EV_PENDING) & 0x01)
+ {
+ litex_txdone(priv);
+
+ /* ACK the pending flag AFTER processing Tx Done */
+
+ putreg8(0x01, LITEX_ETHMAC_SRAM_READER_EV_PENDING);
+ }
+
+ net_unlock();
+
+ /* Re-enable Ethernet interrupts */
+
+ up_enable_irq(LITEX_IRQ_ETHMAC);
+}
+
+/****************************************************************************
+ * Function: litex_emac_interrupt
+ *
+ * Description:
+ * Hardware interrupt handler
+ *
+ * Input Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int litex_emac_interrupt(int irq, void *context, void *arg)
+{
+ struct litex_emac_s *priv = &g_emac;
+
+ /* Disable further Ethernet interrupts. Because Ethernet interrupts are
+ * also disabled if the TX timeout event occurs, there can be no race
+ * condition here.
+ */
+
+ up_disable_irq(LITEX_IRQ_ETHMAC);
+
+ /* Schedule to perform the interrupt processing on the worker thread. */
+
+ work_queue(ETHWORK, &priv->irqwork, litex_emac_interrupt_work, priv, 0);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: litex_linkup
+ *
+ * Description:
+ * Check if the link is up
+ *
+ * Input Parameters:
+ * priv - Reference to the private driver state structure
+ *
+ * Returned Value:
+ * true: The link is up
+ *
+ ****************************************************************************/
+
+static int litex_linkup(struct litex_emac_s *priv)
+{
+ int ret;
+ uint16_t i;
+ uint16_t msr;
+
+ for (i = 0; i < LITEX_WAITLINKTIMEOUT; ++i)
+ {
+ nxsig_usleep(1000);
+
+ ret = litex_phyread(priv, priv->phyaddr, MII_MSR, &msr);
+ if (ret < 0)
+ {
+ nerr("ERROR: Failed to read MSR: %d\n", ret);
+ return ret;
+ }
+
+ if ((msr & MII_MSR_LINKSTATUS) != 0)
+ {
+ return OK;
+ }
+ }
+
+ nerr("ERROR: Timeout to wait for PHY LINK UP\n");
+ return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Function: litex_ifup
+ *
+ * Description:
+ * NuttX Callback: Bring up the EMAC interface when an IP address is
+ * provided
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int litex_ifup(struct net_driver_s *dev)
+{
+ struct litex_emac_s *priv = (struct litex_emac_s *)dev->d_private;
+ int ret;
+
+ ninfo("Bringing up: %d.%d.%d.%d\n",
+ (int)(dev->d_ipaddr & 0xff),
+ (int)((dev->d_ipaddr >> 8) & 0xff),
+ (int)((dev->d_ipaddr >> 16) & 0xff),
+ (int)(dev->d_ipaddr >> 24));
+
+ /* Configure the EMAC interface for normal operation. */
+
+ ninfo("Initialize the EMAC\n");
+ litex_emac_configure(priv);
+
+ /* Initialize for PHY access */
+
+ ret = litex_phyinit(priv);
+ if (ret < 0)
+ {
+ nerr("ERROR: litex_phyinit failed: %d\n", ret);
+ return ret;
+ }
+
+ ninfo("Wait for link up\n");
+
+ ret = litex_linkup(priv);
+ if (ret != 0)
+ {
+ nerr("ERROR: Failed to wait LINK UP error=%d\n", ret);
+ return ret;
+ }
+
+ ninfo("Link detected\n");
+
+ /* Enable normal MAC operation */
+
+ ninfo("Enable normal operation\n");
+
+ /* Enable the EMAC interrupt */
+
+ priv->ifup = true;
+ up_enable_irq(LITEX_IRQ_ETHMAC);
+
+ litex_phydump(priv);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: litex_ifdown
+ *
+ * Description:
+ * NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int litex_ifdown(struct net_driver_s *dev)
+{
+ struct litex_emac_s *priv = (struct litex_emac_s *)dev->d_private;
+ irqstate_t flags;
+
+ ninfo("Taking the network down\n");
+
+ /* Disable the EMAC interrupt */
+
+ flags = enter_critical_section();
+ up_disable_irq(LITEX_IRQ_ETHMAC);
+
+ /* Cancel the TX poll timer and TX timeout timers */
+
+ wd_cancel(&priv->txpoll);
+ wd_cancel(&priv->txtimeout);
+
+ /* Hold the PHY device in reset and mark the interface as "down" */
+
+ putreg32(1, LITEX_ETHPHY_CRG_RESET);
+ priv->ifup = false;
+ leave_critical_section(flags);
+
+ litex_phydump(priv);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: litex_phywriteraw
+ *
+ * Description:
+ * Write 1 word to the MDIO interface.
+ * The hardware requires the data to be bitbashed in.
+ *
+ * Input Parameters:
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void litex_phywriteraw(uint32_t word, uint8_t bitcount)
+{
+ word <<= 32 - bitcount;
+
+ while (bitcount > 0)
+ {
+ if (word & 0x80000000)
+ {
+ putreg32(LITEX_PHY_MDIO_DO | LITEX_PHY_MDIO_OE,
+ LITEX_ETHPHY_MDIO_W);
+ up_udelay(1);
+ putreg32(LITEX_PHY_MDIO_DO | LITEX_PHY_MDIO_OE | LITEX_PHY_MDIO_CK,
+ LITEX_ETHPHY_MDIO_W);
+ up_udelay(1);
+ putreg32(LITEX_PHY_MDIO_DO | LITEX_PHY_MDIO_OE,
+ LITEX_ETHPHY_MDIO_W);
+ }
+ else
+ {
+ putreg32(LITEX_PHY_MDIO_OE,
+ LITEX_ETHPHY_MDIO_W);
+ up_udelay(1);
+ putreg32(LITEX_PHY_MDIO_OE | LITEX_PHY_MDIO_CK,
+ LITEX_ETHPHY_MDIO_W);
+ up_udelay(1);
+ putreg32(LITEX_PHY_MDIO_OE,
+ LITEX_ETHPHY_MDIO_W);
+ }
+
+ word <<= 1;
+ bitcount--;
+ }
+}
+
+/****************************************************************************
+ * Function: litex_phyreadraw
+ *
+ * Description:
+ * Read 1 word from the MDIO interface.
+ * The hardware requires this interface to be bitbashed to get the data out.
+ *
+ * Input Parameters:
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static uint16_t litex_phyreadraw(void)
+{
+ uint16_t word;
+ uint8_t i;
+
+ word = 0;
+
+ for (i = 0; i < 16; ++i)
+ {
+ word <<= 1;
+
+ if (getreg32(LITEX_ETHPHY_MDIO_R) & LITEX_PHY_MDIO_DI)
+ {
+ word |= 1;
+ }
+
+ putreg32(LITEX_PHY_MDIO_CK, LITEX_ETHPHY_MDIO_W);
+ up_udelay(1);
+ putreg32(0x00, LITEX_ETHPHY_MDIO_W);
+ up_udelay(1);
+ }
+
+ return word;
+}
+
+/****************************************************************************
+ * Function: litex_phyturnaround
+ *
+ * Description:
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void litex_phyturnaround(void)
+{
+ int i;
+
+ for (i = 0; i < 2; ++i)
+ {
+ up_udelay(1);
+ putreg32(LITEX_PHY_MDIO_CK, LITEX_ETHPHY_MDIO_W);
+ up_udelay(1);
+ putreg32(0x00, LITEX_ETHPHY_MDIO_W);
+ }
+}
+
+/****************************************************************************
+ * Function: litex_phyread
+ *
+ * Description:
+ * Read a PHY register.
+ *
+ * Input Parameters:
+ * priv - Reference to the private driver state structure.
+ * phyaddr - The PHY device address.
+ * regaddr - The PHY register address.
+ * phyval - The location to return the 16-bit PHY register value.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int litex_phyread(struct litex_emac_s *priv, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t *phyval)
+{
+ putreg32(LITEX_PHY_MDIO_OE, LITEX_ETHPHY_MDIO_W);
+ litex_phywriteraw(LITEX_PHY_MDIO_PREAMBLE, 32);
+ litex_phywriteraw(LITEX_PHY_MDIO_START, 2);
+ litex_phywriteraw(LITEX_PHY_MDIO_READ, 2);
+ litex_phywriteraw(phyaddr, 5);
+ litex_phywriteraw(regaddr, 5);
+ litex_phyturnaround();
+ *phyval = litex_phyreadraw();
+ litex_phyturnaround();
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: litex_phywrite
+ *
+ * Description:
+ * Write to a PHY register.
+ *
+ * Input Parameters:
+ * priv - Reference to the private driver state structure.
+ * phyaddr - The PHY device address.
+ * regaddr - The PHY register address.
+ * phyval - The 16-bit value to write to the PHY register.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int litex_phywrite(struct litex_emac_s *priv, uint8_t phyaddr,
+ uint8_t regaddr, uint16_t phyval)
+{
+ putreg32(LITEX_PHY_MDIO_OE, LITEX_ETHPHY_MDIO_W);
+ litex_phywriteraw(LITEX_PHY_MDIO_PREAMBLE, 32);
+ litex_phywriteraw(LITEX_PHY_MDIO_START, 2);
+ litex_phywriteraw(LITEX_PHY_MDIO_WRITE, 2);
+ litex_phywriteraw(phyaddr, 5);
+ litex_phywriteraw(regaddr, 5);
+ litex_phywriteraw(LITEX_PHY_MDIO_TURNAROUND, 2);
+ litex_phywriteraw(phyval, 16);
+ litex_phyturnaround();
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Function: litex_phydump
+ *
+ * Description:
+ * Dump the contents of PHY registers
+ *
+ * Input Parameters:
+ * priv - Reference to the private driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO)
+static void litex_phydump(struct litex_emac_s *priv)
+{
+ uint16_t phyval;
+
+ ninfo("%s: MII Registers (Address %02x)\n", BOARD_PHY_NAME, priv->phyaddr);
+
+ litex_phyread(priv, priv->phyaddr, MII_MCR, &phyval);
+ ninfo(" MCR: %04x\n", phyval);
+ litex_phyread(priv, priv->phyaddr, MII_MSR, &phyval);
+ ninfo(" MSR: %04x\n", phyval);
+ litex_phyread(priv, priv->phyaddr, MII_ADVERTISE, &phyval);
+ ninfo(" ADVERTISE: %04x\n", phyval);
+ litex_phyread(priv, priv->phyaddr, MII_LPA, &phyval);
+ ninfo(" LPA: %04x\n", phyval);
+ litex_phyread(priv, priv->phyaddr, MII_EXPANSION, &phyval);
+ ninfo(" EXPANSION: %04x\n", phyval);
+ litex_phyread(priv, priv->phyaddr, MII_DP83848C_10BTSCR, &phyval);
+ ninfo(" 10BTCR: %04x\n", phyval);
+ litex_phyread(priv, priv->phyaddr, BOARD_PHY_STATUS, &phyval);
+ ninfo(" PHYSR: %04x\n", phyval);
+
+ if (phyval == 0xffff)
+ {
+ return;
+ }
+
+ if (BOARD_PHY_ISDUPLEX(phyval))
+ {
+ ninfo("%s: Full duplex\n", BOARD_PHY_NAME);
+ }
+ else
+ {
+ ninfo("%s: Half duplex\n", BOARD_PHY_NAME);
+ }
+
+ if (BOARD_PHY_10BASET(phyval))
+ {
+ /* 10 Mbps */
+
+ ninfo("%s: 10 Base-T\n", BOARD_PHY_NAME);
+ }
+ else if (BOARD_PHY_100BASET(phyval))
+ {
+ /* 100 Mbps */
+
+ ninfo("%s: 100 Base-T\n", BOARD_PHY_NAME);
+ }
+ else
+ {
+ /* This might happen if Autonegotiation did not complete(?) */
+
+ nerr("ERROR: Neither 10- nor 100-BaseT reported: PHY STATUS=%04x\n",
+ phyval);
+ }
+}
+#endif
+
+/****************************************************************************
+ * Function: litex_phyfind
+ *
+ * Description:
+ * Verify the PHY address.
+ *
+ * Input Parameters:
+ * priv - Reference to the private driver state structure
+ * phyaddr - MDIO address to try and find confgiured PHY IC
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int litex_phyfind(struct litex_emac_s *priv, uint8_t phyaddr)
+{
+ uint16_t phyval[2];
+ uint32_t oui;
+ uint16_t model;
+ uint16_t revision;
+
+ int ret;
+
+ ninfo("%s: Find a valid PHY at address: %d\n",
+ BOARD_PHY_NAME, phyaddr);
+
+ /* Read PHYID1 */
+
+ ret = litex_phyread(priv, phyaddr, MII_PHYID1, &phyval[0]);
+
+ /* Verify PHYID1 */
+
+ if (ret != OK)
+ {
+ nerr("ERROR: litex_phyread failed for PHY address %02x: %d\n",
+ phyaddr, ret);
+ return -EIO;
+ }
+
+ if (phyval[0] != BOARD_PHYID1)
+ {
+ nerr("ERROR: unexpected PHYID - Expected: %02x Got: %02x\n",
+ BOARD_PHYID1, phyval[0]);
+ return -ESRCH;
+ }
+
+ /* Read PHYID2 */
+
+ ret = litex_phyread(priv, phyaddr, MII_PHYID2, &phyval[1]);
+
+ /* Verify PHYID2 */
+
+ if (ret != OK)
+ {
+ nerr("ERROR: litex_phyread failed for PHY address %02x: %d\n",
+ phyaddr, ret);
+ return -EIO;
+ }
+
+ if (phyval[1] != BOARD_PHYID2)
+ {
+ nerr("ERROR: unexpected PHYID - Expected: %02x Got: %02x\n",
+ BOARD_PHYID2, phyval[1]);
+ return -ESRCH;
+ }
+
+ oui = ((phyval[0] << 16) | (phyval[1] & 0xfc00)) >> 10;
+ model = (phyval[1] & 0x03f0) >> 4;
+ revision = (phyval[1] & 0x000f);
+
+ ninfo("%s: PHY Found - OUI: 0x%04" PRIx32 "MODEL: %u REV: %u\n",
+ BOARD_PHY_NAME, oui, model, revision);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: litex_phyinit
+ *
+ * Description:
+ * Configure the PHY
+ *
+ * Input Parameters:
+ * priv - Reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_phyinit(struct litex_emac_s *priv)
+{
+ int ret;
+
+ /* Reset PHY */
+
+ putreg32(1, LITEX_ETHPHY_CRG_RESET);
+ nxsig_usleep(LITEX_PHY_RESETTIMEOUT);
+ putreg32(0, LITEX_ETHPHY_CRG_RESET);
+ nxsig_usleep(LITEX_PHY_RESETTIMEOUT);
+
+ /* Check the PHY responds at configured address */
+
+ priv->phyaddr = CONFIG_LITEX_EMAC_PHYADDR;
+
+ ninfo("PHY ADDR: %d\n", priv->phyaddr);
+
+ ret = litex_phyfind(priv, priv->phyaddr);
+ if (ret < 0)
+ {
+ nerr("ERROR: litex_phyfind failed: %d\n", ret);
+ return ret;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: litex_ioctl
+ *
+ * Description:
+ * PHY ioctl command handler
+ *
+ * Input Parameters:
+ * dev - Reference to the NuttX driver state structure
+ * cmd - ioctl command
+ * arg - Argument accompanying the command
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int litex_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+ struct litex_emac_s *priv = (struct litex_emac_s *)dev->d_private;
+#endif
+ int ret;
+
+ switch (cmd)
+ {
+#ifdef CONFIG_NETDEV_PHY_IOCTL
+ case SIOCGMIIPHY: /* Get MII PHY address */
+ {
+ struct mii_ioctl_data_s *req =
+ (struct mii_ioctl_data_s *)((uintptr_t)arg);
+ req->phy_id = priv->phyaddr;
+ ret = OK;
+ }
+ break;
+
+ case SIOCGMIIREG: /* Get register from MII PHY */
+ {
+ struct mii_ioctl_data_s *req =
+ (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+ /* Read from the requested register */
+
+ ret = litex_phyread(priv, req->phy_id,
+ req->reg_num, &req->val_out);
+ }
+ break;
+
+ case SIOCSMIIREG: /* Set register in MII PHY */
+ {
+ struct mii_ioctl_data_s *req =
+ (struct mii_ioctl_data_s *)((uintptr_t)arg);
+
+ /* Write to the requested register */
+
+ ret = litex_phywrite(priv, req->phy_id,
+ req->reg_num, req->val_in);
+ }
+ break;
+#endif /* ifdef CONFIG_NETDEV_PHY_IOCTL */
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Function: litex_emac_configure
+ *
+ * Description:
+ * Configure the EMAC interface for normal operation.
+ *
+ * Input Parameters:
+ * priv - Reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int litex_emac_configure(struct litex_emac_s *priv)
+{
+ /* Clear pending events */
+
+ putreg8(0x01, LITEX_ETHMAC_SRAM_READER_EV_PENDING);
+ putreg8(0x01, LITEX_ETHMAC_SRAM_WRITER_EV_PENDING);
+
+ /* Enable Tx and Rx interrupts */
+
+ putreg8(0x01, LITEX_ETHMAC_SRAM_READER_EV_ENABLE);
+ putreg8(0x01, LITEX_ETHMAC_SRAM_WRITER_EV_ENABLE);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: litex_buffer_init
+ *
+ * Description:
+ * Allocate aligned TX and RX descriptors and buffers. For the case of
+ * pre-allocated structures, the function degenerates to a few assignments.
+ *
+ * Input Parameters:
+ * priv - Reference to the private driver state structure
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ * Called very early in the initialization sequence.
+ *
+ ****************************************************************************/
+
+static int litex_buffer_init(struct litex_emac_s *priv)
+{
+ priv->txslot = 0;
+ priv->dev.d_buf = g_buffer;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Function: litex_ethinitialize
+ *
+ * Description:
+ * Initialize the Ethernet driver for one interface.
+ *
+ * Returned Value:
+ * OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static void litex_ethinitialize(void)
+{
+ struct litex_emac_s *priv = &g_emac;
+ int ret;
+
+ /* Initialize the driver structure */
+
+ memset(priv, 0, sizeof(struct litex_emac_s));
+ priv->dev.d_ifup = litex_ifup; /* I/F up (new IP address) callback */
+ priv->dev.d_ifdown = litex_ifdown; /* I/F down callback */
+ priv->dev.d_txavail = litex_txavail; /* New TX data callback */
+#ifdef CONFIG_NETDEV_IOCTL
+ priv->dev.d_ioctl = litex_ioctl; /* Support PHY ioctl() calls */
+#endif
+ priv->dev.d_private = &g_emac; /* Used to recover private state from dev */
+
+ /* Init buffers */
+
+ ret = litex_buffer_init(priv);
+ if (ret < 0)
+ {
+ nerr("ERROR: litex_buffer_initialize failed: %d\n", ret);
+ return;
+ }
+
+ /* Attach the IRQ to the driver. It will not be enabled at the AIC until
+ * the interface is in the 'up' state.
+ */
+
+ ret = irq_attach(LITEX_IRQ_ETHMAC, litex_emac_interrupt, NULL);
+ if (ret < 0)
+ {
+ nerr("ERROR: Failed to attach the handler to the IRQ%d\n",
+ LITEX_IRQ_ETHMAC);
+ }
+
+ /* Put the interface in the down state */
+
+ ret = litex_ifdown(&priv->dev);
+ if (ret < 0)
+ {
+ nerr("ERROR: Failed to put the interface in the down state: %d\n",
+ ret);
+ }
+
+ /* Register the device with the OS so that socket IOCTLs can be performed */
+
+ ret = netdev_register(&priv->dev, NET_LL_ETHERNET);
+ if (ret >= 0)
+ {
+ return;
+ }
+
+ nerr("ERROR: netdev_register() failed: %d\n", ret);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: riscv_netinitialize
+ *
+ * Description:
+ * This is the "standard" network initialization logic called from the
+ * low-level initialization logic in riscv_initialize.c.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returned Value:
+ * None.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_NETDEV_LATEINIT)
+void riscv_netinitialize(void)
+{
+ litex_ethinitialize();
+}
+#endif
+
+#endif //CONFIG_LITEX_ETHMAC