From 54856cfa5eab26bb047e73242f267cae8dbd0d38 Mon Sep 17 00:00:00 2001 From: Jukka Laitinen Date: Tue, 30 Apr 2019 08:43:39 -0600 Subject: [PATCH] arch/arm/src/stm32h7/: Add stm32h7 ethernet driver. This is the initial push for the ethernet driver. The driver has been tested to be working on a nucleo board. This is still WIP, it doesn't for example do MAC filtering on HW level, but just receives all ethernet packets. --- arch/arm/include/stm32h7/chip.h | 20 +- arch/arm/src/stm32h7/Kconfig | 207 + arch/arm/src/stm32h7/Make.defs | 4 + arch/arm/src/stm32h7/chip/stm32_ethernet.h | 676 +++ arch/arm/src/stm32h7/stm32_ethernet.c | 4410 ++++++++++++++++++++ arch/arm/src/stm32h7/stm32_ethernet.h | 119 + arch/arm/src/stm32h7/stm32h7x3xx_rcc.c | 7 + configs/nucleo-h743zi/include/board.h | 6 + 8 files changed, 5441 insertions(+), 8 deletions(-) create mode 100644 arch/arm/src/stm32h7/chip/stm32_ethernet.h create mode 100644 arch/arm/src/stm32h7/stm32_ethernet.c create mode 100644 arch/arm/src/stm32h7/stm32_ethernet.h diff --git a/arch/arm/include/stm32h7/chip.h b/arch/arm/include/stm32h7/chip.h index ab3e999dfcf..a1b7ffe5269 100644 --- a/arch/arm/include/stm32h7/chip.h +++ b/arch/arm/include/stm32h7/chip.h @@ -122,17 +122,21 @@ /* Diversification based on Family and package */ -// TODO: -// #if defined(CONFIG_STM32F7_HAVE_FMC) -// # define STM32F7_NFMC 1 /* Have FMC memory controller */ -// #else -// # define STM32F7_NFMC 0 /* No FMC memory controller */ -// #endif +#if defined(CONFIG_STM32H7_HAVE_ETHERNET) +# define STM32H7_NETHERNET 1 /* 100/100 Ethernet MAC */ +#else +# define STM32H7_NETHERNET 0 /* No 100/100 Ethernet MAC */ +#endif -/* NVIC priority levels *************************************************************/ +#if defined(CONFIG_STM32F7_HAVE_FMC) +# define STM32F7_NFMC 1 /* Have FMC memory controller */ +#else +# define STM32F7_NFMC 0 /* No FMC memory controller */ +#endif + +/* NVIC priority levels **********************************************************o***/ /* 16 Programmable interrupt levels */ -// TODO: check this #define NVIC_SYSH_PRIORITY_MIN 0xf0 /* All bits set in minimum priority */ #define NVIC_SYSH_PRIORITY_DEFAULT 0x80 /* Midpoint is the default */ #define NVIC_SYSH_PRIORITY_MAX 0x00 /* Zero is maximum priority */ diff --git a/arch/arm/src/stm32h7/Kconfig b/arch/arm/src/stm32h7/Kconfig index 2405f9e3325..08b2af222d8 100644 --- a/arch/arm/src/stm32h7/Kconfig +++ b/arch/arm/src/stm32h7/Kconfig @@ -31,6 +31,7 @@ config STM32H7_STM32H7X3XX select ARMV7M_HAVE_DCACHE select ARMV7M_HAVE_ITCM select ARMV7M_HAVE_DTCM + select STM32H7_HAVE_ETHERNET select STM32H7_HAVE_SPI4 select STM32H7_HAVE_SPI5 select STM32H7_HAVE_SPI6 @@ -78,6 +79,10 @@ menu "STM32H7 Peripheral Selection" # These "hidden" settings determine is a peripheral option is available for the # selection MCU +config STM32H7_HAVE_ETHERNET + bool + default n + config STM32H7_HAVE_SPI4 bool default n @@ -161,6 +166,13 @@ config STM32H7_BDMA select STM32H7_DMA select ARCH_DMA +config STM32H7_ETHMAC + bool "Ethernet MAC" + default n + depends on STM32H7_HAVE_ETHERNET + select NETDEVICES + select ARCH_HAVE_PHY + config STM32H7_OTGFS bool "OTG FS" default n @@ -860,4 +872,199 @@ config STM32H7_CUSTOM_CLOCKCONFIG ---help--- Enables special, board-specific STM32 clock configuration. +menu "Ethernet MAC configuration" + depends on STM32H7_ETHMAC + +config STM32H7_PHYADDR + int "PHY address" + default 0 + ---help--- + The 5-bit address of the PHY on the board. Default: 1 + +config STM32H7_PHYINIT + bool "Board-specific PHY Initialization" + default n + ---help--- + Some boards require specialized initialization of the PHY before it can be used. + This may include such things as configuring GPIOs, resetting the PHY, etc. If + STM32H7_PHYINIT is defined in the configuration then the board specific logic must + provide stm32_phyinitialize(); The STM32 Ethernet driver will call this function + one time before it first uses the PHY. + +config STM32H7_MII + bool "Use MII interface" + default n + ---help--- + Support Ethernet MII interface. + +choice + prompt "MII clock configuration" + default STM32H7_MII_EXTCLK + depends on STM32H7_MII + +config STM32H7_MII_MCO1 + bool "Use MC01 as MII clock" + ---help--- + Use MCO1 to clock the MII interface. + +config STM32H7_MII_MCO2 + bool "Use MC02 as MII clock" + ---help--- + Use MCO2 to clock the MII interface. + +config STM32H7_MII_EXTCLK + bool "External MII clock" + ---help--- + Clocking is provided by external logic. + +endchoice + +config STM32H7_AUTONEG + bool "Use autonegotiation" + default y + ---help--- + Use PHY autonegotiation to determine speed and mode + +config STM32H7_ETHFD + bool "Full duplex" + default n + depends on !STM32H7_AUTONEG + ---help--- + If STM32H7_AUTONEG is not defined, then this may be defined to select full duplex + mode. Default: half-duplex + +config STM32H7_ETH100MBPS + bool "100 Mbps" + default n + depends on !STM32H7_AUTONEG + ---help--- + If STM32H7_AUTONEG is not defined, then this may be defined to select 100 MBps + speed. Default: 10 Mbps + +config STM32H7_PHYSR + int "PHY Status Register Address (decimal)" + depends on STM32H7_AUTONEG + ---help--- + This must be provided if STM32H7_AUTONEG is defined. The PHY status register + address may diff from PHY to PHY. This configuration sets the address of + the PHY status register. + +config STM32H7_PHYSR_ALTCONFIG + bool "PHY Status Alternate Bit Layout" + default n + depends on STM32H7_AUTONEG + ---help--- + Different PHYs present speed and mode information in different ways. Some + will present separate information for speed and mode (this is the default). + Those PHYs, for example, may provide a 10/100 Mbps indication and a separate + full/half duplex indication. This options selects an alternative representation + where speed and mode information are combined. This might mean, for example, + separate bits for 10HD, 100HD, 10FD and 100FD. + +config STM32H7_PHYSR_SPEED + hex "PHY Speed Mask" + depends on STM32H7_AUTONEG && !STM32H7_PHYSR_ALTCONFIG + ---help--- + This must be provided if STM32H7_AUTONEG is defined. This provides bit mask + for isolating the 10 or 100MBps speed indication. + +config STM32H7_PHYSR_100MBPS + hex "PHY 100Mbps Speed Value" + depends on STM32H7_AUTONEG && !STM32H7_PHYSR_ALTCONFIG + ---help--- + This must be provided if STM32H7_AUTONEG is defined. This provides the value + of the speed bit(s) indicating 100MBps speed. + +config STM32H7_PHYSR_MODE + hex "PHY Mode Mask" + depends on STM32H7_AUTONEG && !STM32H7_PHYSR_ALTCONFIG + ---help--- + This must be provided if STM32H7_AUTONEG is defined. This provide bit mask + for isolating the full or half duplex mode bits. + +config STM32H7_PHYSR_FULLDUPLEX + hex "PHY Full Duplex Mode Value" + depends on STM32H7_AUTONEG && !STM32H7_PHYSR_ALTCONFIG + ---help--- + This must be provided if STM32H7_AUTONEG is defined. This provides the + value of the mode bits indicating full duplex mode. + +config STM32H7_PHYSR_ALTMODE + hex "PHY Mode Mask" + depends on STM32H7_AUTONEG && STM32H7_PHYSR_ALTCONFIG + ---help--- + This must be provided if STM32H7_AUTONEG is defined. This provide bit mask + for isolating the speed and full/half duplex mode bits. + +config STM32H7_PHYSR_10HD + hex "10MBase-T Half Duplex Value" + depends on STM32H7_AUTONEG && STM32H7_PHYSR_ALTCONFIG + ---help--- + This must be provided if STM32H7_AUTONEG is defined. This is the value + under the bit mask that represents the 10Mbps, half duplex setting. + +config STM32H7_PHYSR_100HD + hex "100Base-T Half Duplex Value" + depends on STM32H7_AUTONEG && STM32H7_PHYSR_ALTCONFIG + ---help--- + This must be provided if STM32H7_AUTONEG is defined. This is the value + under the bit mask that represents the 100Mbps, half duplex setting. + +config STM32H7_PHYSR_10FD + hex "10Base-T Full Duplex Value" + depends on STM32H7_AUTONEG && STM32H7_PHYSR_ALTCONFIG + ---help--- + This must be provided if STM32H7_AUTONEG is defined. This is the value + under the bit mask that represents the 10Mbps, full duplex setting. + +config STM32H7_PHYSR_100FD + hex "100Base-T Full Duplex Value" + depends on STM32H7_AUTONEG && STM32H7_PHYSR_ALTCONFIG + ---help--- + This must be provided if STM32H7_AUTONEG is defined. This is the value + under the bit mask that represents the 100Mbps, full duplex setting. + +config STM32H7_ETH_PTP + bool "Precision Time Protocol (PTP)" + default n + ---help--- + Precision Time Protocol (PTP). Not supported but some hooks are indicated + with this condition. + +config STM32H7_RMII + bool + default y if !STM32H7_MII + +choice + prompt "RMII clock configuration" + default STM32H7_RMII_EXTCLK + depends on STM32H7_RMII + +config STM32H7_RMII_MCO1 + bool "Use MC01 as RMII clock" + ---help--- + Use MCO1 to clock the RMII interface. + +config STM32H7_RMII_MCO2 + bool "Use MC02 as RMII clock" + ---help--- + Use MCO2 to clock the RMII interface. + +config STM32H7_RMII_EXTCLK + bool "External RMII clock" + ---help--- + Clocking is provided by external logic. + +endchoice # RMII clock configuration + +config STM32H7_ETHMAC_REGDEBUG + bool "Register-Level Debug" + default n + depends on DEBUG_NET_INFO + ---help--- + Enable very low-level register access debug. Depends on + CONFIG_DEBUG_FEATURES. + +endmenu # Ethernet MAC configuration + endif # ARCH_CHIP_STM32H7 diff --git a/arch/arm/src/stm32h7/Make.defs b/arch/arm/src/stm32h7/Make.defs index 6d7984ff25d..0f0bd9a3be1 100644 --- a/arch/arm/src/stm32h7/Make.defs +++ b/arch/arm/src/stm32h7/Make.defs @@ -125,3 +125,7 @@ endif ifeq ($(CONFIG_USBHOST),y) CHIP_CSRCS += stm32_otghost.c endif + +ifeq ($(CONFIG_STM32H7_ETHMAC),y) +CHIP_CSRCS += stm32_ethernet.c +endif diff --git a/arch/arm/src/stm32h7/chip/stm32_ethernet.h b/arch/arm/src/stm32h7/chip/stm32_ethernet.h new file mode 100644 index 00000000000..4f1dfe5f155 --- /dev/null +++ b/arch/arm/src/stm32h7/chip/stm32_ethernet.h @@ -0,0 +1,676 @@ +/**************************************************************************************************** + * arch/arm/src/stm32h7/chip/stm32_ethernet.h + * + * Authors: Jukka Laitinen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H7_CHIP_STM32_ETHERNET_H +#define __ARCH_ARM_SRC_STM32H7_CHIP_STM32_ETHERNET_H + +/**************************************************************************************************** + * Included Files + ****************************************************************************************************/ + +#include + +/* Content of this file requires verification before it is used with other + * families + */ + +#if defined(CONFIG_STM32H7_STM32H7X3XX) + +/**************************************************************************************************** + * Pre-processor Definitions + ****************************************************************************************************/ + +/* Register Offsets *********************************************************************************/ + +/* MAC Registers */ + +#define STM32_ETH_MACCR_OFFSET 0x0000 /* Ethernet MAC configuration register */ +#define STM32_ETH_MAECR_OFFSET 0x0004 /* Ethernet MAC Extended operating mode configuration register */ +#define STM32_ETH_MACPFR_OFFSET 0x0008 /* Ethernet MAC Packet filtering control register */ +#define STM32_ETH_MACWTR_OFFSET 0x000C /* Ethernet MAC Watchdog timeout register */ +#define STM32_ETH_MACHT0R_OFFSET 0x0010 /* Ethernet MAC hash table high register */ +#define STM32_ETH_MACHT1R_OFFSET 0x0014 /* Ethernet MAC hash table low register */ +#define STM32_ETH_MACVTR_OFFSET 0x0050 /* Ethernet MAC VLAN tag register */ +#define STM32_ETH_MACQTXFCR_OFFSET 0x0070 /* Ethernet MAC Tx Queue flow control register */ +#define STM32_ETH_MACRXFCR_OFFSET 0x0090 /* Ethernet MAC Tx Queue flow control register */ + +#define STM32_ETH_MACISR_OFFSET 0x00B0 /* Ethernet MAC interrupt status register */ +#define STM32_ETH_MACIER_OFFSET 0x00B4 /* Ethernet MAC interrupt enable register */ + +#define STM32_ETH_MACMDIOAR_OFFSET 0x0200 /* Ethernet MAC MDIO address register */ +#define STM32_ETH_MACMDIODR_OFFSET 0x0204 /* Ethernet MAC MDIO data register */ +#define STM32_ETH_MACA0LR_OFFSET 0x0304 /* Address 0 low register */ +#define STM32_ETH_MACA0HR_OFFSET 0x0308 /* Address 0 high register */ + +/* MTL Registers */ + +#define STM32_ETH_MTLOMR_OFFSET 0x0C00 /* Operating mode Register */ +#define STM32_ETH_MTLISR_OFFSET 0x0C20 /* Interrupt status Register */ +#define STM32_ETH_MTLTXQOMR_OFFSET 0x0D00 /* Tx queue operating mode Register */ +#define STM32_ETH_MTLTXQUR_OFFSET 0x0D04 /* Tx queue underflow register */ +#define STM32_ETH_MTLTXQDR_OFFSET 0x0D08 /* Tx queue debug register */ +#define STM32_ETH_MTLQICSR_OFFSET 0x0D2C /* Queue interrupt control status Register */ +#define STM32_ETH_MTLRXQOMR_OFFSET 0x0D30 /* Rx queue operating mode register */ +#define STM32_ETH_MTLRXQMPOCR_OFFSET 0x0D34 /* Rx queue missed packet and overflow counter register */ +#define STM32_ETH_MTLRXQDR_OFFSE 0x0D38 /* Rx queue debug register */ + +/* DMA Registers */ + +#define STM32_ETH_DMAMR_OFFSET 0x1000 /* Ethernet DMA Mode register */ +#define STM32_ETH_DMASBMR_OFFSET 0x1004 /* Ethernet DMA system bus mode register */ +#define STM32_ETH_DMAISR_OFFSET 0x1008 /* Ethernet DMA Interrupt status register */ +#define STM32_ETH_DMADSR_OFFSET 0x100C /* Ethernet DMA Debug status register */ +#define STM32_ETH_DMACCR_OFFSET 0x1100 /* Ethernet DMA Channel control register */ +#define STM32_ETH_DMACTXCR_OFFSET 0x1104 /* Ethernet DMA Channel transmit control register */ +#define STM32_ETH_DMACRXCR_OFFSET 0x1108 /* Ethernet DMA Channel receive control register */ +#define STM32_ETH_DMACTXDLAR_OFFSET 0x1114 /* Ethernet DMA Channel Tx descriptor list address register */ +#define STM32_ETH_DMACRXDLAR_OFFSET 0x111C /* Ethernet DMA Channel Rx descriptor list address register */ +#define STM32_ETH_DMACTXDTPR_OFFSET 0x1120 /* Ethernet DMA Channel Tx descriptor tail pointer register */ +#define STM32_ETH_DMACRXDTPR_OFFSET 0x1128 /* Ethernet DMA Channel Rx descriptor tail pointer register */ +#define STM32_ETH_DMACTXRLR_OFFSET 0x112C /* Ethernet DMA Channel Tx descriptor ring length register */ +#define STM32_ETH_DMACRXRLR_OFFSET 0x1130 /* Ethernet DMA Channel Rx descriptor ring length register */ +#define STM32_ETH_DMACIER_OFFSET 0x1134 /* Ethernet DMA Channel interrupt enable register */ +#define STM32_ETH_DMACRXIWTR_OFFSET 0x1138 /* Ethernet DMA Channel Rx interrupt wdt register */ +#define STM32_ETH_DMACCATXDR_OFFSET 0x1144 /* Ethernet DMA Channel current application transmit descriptor register */ +#define STM32_ETH_DMACCARXDR_OFFSET 0x114C /* Ethernet DMA Channel current application receive descriptor register */ +#define STM32_ETH_DMACCATXBR_OFFSET 0x1154 /* Ethernet DMA Channel current application transmit buffer register */ +#define STM32_ETH_DMACCARXBR_OFFSET 0x115C /* Ethernet DMA Channel current application receive buffer register */ +#define STM32_ETH_DMACSR_OFFSET 0x1160 /* Ethernet DMA Channel status register */ +#define STM32_ETH_DMACMFCR_OFFSET 0x116C /* Ethernet DMA Channel missed frame count register */ + +/* Register Base Addresses **************************************************************************/ + +/* MAC Registers */ + +#define STM32_ETH_MACCR (STM32_EMAC_BASE+STM32_ETH_MACCR_OFFSET) +#define STM32_ETH_MACPFR (STM32_EMAC_BASE+STM32_ETH_MACPFR_OFFSET) +#define STM32_ETH_MACHT0R (STM32_EMAC_BASE+STM32_ETH_MACHT0R_OFFSET) +#define STM32_ETH_MACHT1R (STM32_EMAC_BASE+STM32_ETH_MACHT1R_OFFSET) +#define STM32_ETH_MACVTR (STM32_EMAC_BASE+STM32_ETH_MACVTR_OFFSET) +#define STM32_ETH_MACQTXFCR (STM32_EMAC_BASE+STM32_ETH_MACQTXFCR_OFFSET) +#define STM32_ETH_MACRXFCR (STM32_EMAC_BASE+STM32_ETH_MACRXFCR_OFFSET) +#define STM32_ETH_MACISR (STM32_EMAC_BASE+STM32_ETH_MACISR_OFFSET) +#define STM32_ETH_MACIER (STM32_EMAC_BASE+STM32_ETH_MACIER_OFFSET) + +#define STM32_ETH_MACMDIOAR (STM32_EMAC_BASE+STM32_ETH_MACMDIOAR_OFFSET) +#define STM32_ETH_MACMDIODR (STM32_EMAC_BASE+STM32_ETH_MACMDIODR_OFFSET) +#define STM32_ETH_MACA0LR (STM32_EMAC_BASE+STM32_ETH_MACA0LR_OFFSET) +#define STM32_ETH_MACA0HR (STM32_EMAC_BASE+STM32_ETH_MACA0HR_OFFSET) + +/* MTL Registers */ + +#define STM32_ETH_MTLOMR (STM32_EMAC_BASE+STM32_ETH_MTLOMR_OFFSET) +#define STM32_ETH_MTLISR (STM32_EMAC_BASE+STM32_ETH_MTLISR_OFFSET) +#define STM32_ETH_MTLTXQOMR (STM32_EMAC_BASE+STM32_ETH_MTLTXQOMR_OFFSET) +#define STM32_ETH_MTLTXQUR (STM32_EMAC_BASE+STM32_ETH_MTLTXQUR_OFFSET) +#define STM32_ETH_MTLTXQDR (STM32_EMAC_BASE+STM32_ETH_MTLTXQDR_OFFSET) +#define STM32_ETH_MTLQICSR (STM32_EMAC_BASE+STM32_ETH_MTLQICSR_OFFSET) +#define STM32_ETH_MTLRXQOMR (STM32_EMAC_BASE+STM32_ETH_MTLRXQOMR_OFFSET) +#define STM32_ETH_MTLRXQMPOCR (STM32_EMAC_BASE+STM32_ETH_MTLRXQMPOCR_OFFSET) +#define STM32_ETH_MTLRXQDR (STM32_EMAC_BASE+STM32_ETH_MTLRXQDR_OFFSE) + +/* DMA Registers */ + +#define STM32_ETH_DMAMR (STM32_EMAC_BASE+STM32_ETH_DMAMR_OFFSET) +#define STM32_ETH_DMASBMR (STM32_EMAC_BASE+STM32_ETH_DMASBMR_OFFSET) +#define STM32_ETH_DMAISR (STM32_EMAC_BASE+STM32_ETH_DMAISR_OFFSET) +#define STM32_ETH_DMADSR (STM32_EMAC_BASE+STM32_ETH_DMADSR_OFFSET) +#define STM32_ETH_DMACCR (STM32_EMAC_BASE+STM32_ETH_DMACCR_OFFSET) +#define STM32_ETH_DMACTXCR (STM32_EMAC_BASE+STM32_ETH_DMACTXCR_OFFSET) +#define STM32_ETH_DMACRXCR (STM32_EMAC_BASE+STM32_ETH_DMACRXCR_OFFSET) +#define STM32_ETH_DMACTXDLAR (STM32_EMAC_BASE+STM32_ETH_DMACTXDLAR_OFFSET) +#define STM32_ETH_DMACRXDLAR (STM32_EMAC_BASE+STM32_ETH_DMACRXDLAR_OFFSET) +#define STM32_ETH_DMACTXDTPR (STM32_EMAC_BASE+STM32_ETH_DMACTXDTPR_OFFSET) +#define STM32_ETH_DMACRXDTPR (STM32_EMAC_BASE+STM32_ETH_DMACRXDTPR_OFFSET) +#define STM32_ETH_DMACTXRLR (STM32_EMAC_BASE+STM32_ETH_DMACTXRLR_OFFSET) +#define STM32_ETH_DMACRXRLR (STM32_EMAC_BASE+STM32_ETH_DMACRXRLR_OFFSET) +#define STM32_ETH_DMACIER (STM32_EMAC_BASE+STM32_ETH_DMACIER_OFFSET) +#define STM32_ETH_DMACRXIWTR (STM32_EMAC_BASE+STM32_ETH_DMACRXIWTR_OFFSET) +#define STM32_ETH_DMACCATXDR (STM32_EMAC_BASE+STM32_ETH_DMACCATXDR_OFFSET) +#define STM32_ETH_DMACCARXDR (STM32_EMAC_BASE+STM32_ETH_DMACCARXDR_OFFSET) +#define STM32_ETH_DMACCATXBR (STM32_EMAC_BASE+STM32_ETH_DMACCATXBR_OFFSET) +#define STM32_ETH_DMACCARXBR (STM32_EMAC_BASE+STM32_ETH_DMACCARXBR_OFFSET) +#define STM32_ETH_DMACSR (STM32_EMAC_BASE+STM32_ETH_DMACSR_OFFSET) +#define STM32_ETH_DMACMFCR (STM32_EMAC_BASE+STM32_ETH_DMACMFCR_OFFSET) + +/* Register Bit-Field Definitions *******************************************************************/ + +/* MAC Registers */ + +/* Ethernet MAC configuration register */ + +#define ETH_MACCR_RE (1 << 0) /* Bit 0: Receiver enable */ +#define ETH_MACCR_TE (1 << 1) /* Bit 1: Transmitter enable */ +#define ETH_MACCR_PRELEN_SHIFT (2) /* Bits 2-3: Preample length for transmit packets */ +#define ETH_MACCR_PRELEN_MASK (3 << ETH_MACCR_PRELEN_SHIFT) +# define ETH_MACCR_PRELEN_7 (0 << ETH_MACCR_PRELEN_SHIFT) /* 00: 7 bytes of preamble */ +# define ETH_MACCR_PRELEN_5 (1 << ETH_MACCR_PRELEN_SHIFT) /* 01: 5 bytes of preamble */ +# define ETH_MACCR_PRELEN_3 (2 << ETH_MACCR_PRELEN_SHIFT) /* 10: 3 bytes of preamble */ +#define ETH_MACCR_DC (1 << 4) /* Bit 4: Deferral check */ +#define ETH_MACCR_BL_SHIFT (5) /* Bits 5-6: Back-off limit */ +#define ETH_MACCR_BL_MASK (3 << ETH_MACCR_BL_SHIFT) +# define ETH_MACCR_BL_10 (0 << ETH_MACCR_BL_SHIFT) /* 00: k = min (n, 10) */ +# define ETH_MACCR_BL_8 (1 << ETH_MACCR_BL_SHIFT) /* 01: k = min (n, 8) */ +# define ETH_MACCR_BL_4 (2 << ETH_MACCR_BL_SHIFT) /* 10: k = min (n, 4) */ +# define ETH_MACCR_BL_1 (3 << ETH_MACCR_BL_SHIFT) /* 11: k = min (n, 1) */ +#define ETH_MACCR_DR (1 << 8) /* Bit 8: Retry disable */ +#define ETH_MACCR_DCRS (1 << 9) /* Bit 9: Carrier sense disable */ +#define ETH_MACCR_DO (1 << 10) /* Bit 10: Disable receive own */ +#define ETH_MACCR_LM (1 << 12) /* Bit 12: Loopback mode */ +#define ETH_MACCR_DM (1 << 13) /* Bit 13: Duplex mode */ +#define ETH_MACCR_FES (1 << 14) /* Bit 14: Fast Ethernet speed */ +#define ETH_MACCR_JE (1 << 16) /* Bit 16: Jumbo packet enable */ +#define ETH_MACCR_JD (1 << 17) /* Bit 17: Jabber disable */ +#define ETH_MACCR_WD (1 << 19) /* Bit 19: Watchdog disable */ +#define ETH_MACCR_ACS (1 << 20) /* Bit 20: Automatic pad/CRC stripping */ +#define ETH_MACCR_CST (1 << 21) /* Bit 21: CRC stripping for Type frames */ +#define ETH_MACCR_S2KP (1 << 22) /* Bit 22: IEEE 802.3as Support for 2K Packets */ +#define ETH_MACCR_GPSLCE (1 << 23) /* Bit 23: Giant Packet Size Limit Control Enable */ +#define ETH_MACCR_IPG_SHIFT (24) /* Bits 24-26: Inter-packet gap */ +#define ETH_MACCR_IPG_MASK (7 << ETH_MACCR_IPG_SHIFT) +# define ETH_MACCR_IPG(n) ((12-((n) >> 3)) << ETH_MACCR_IPG_SHIFT) /* n bit times, n=40,48,..96 */ +#define ETH_MACCR_IPC (1 << 27) /* Bit 27: IPv4 checksum offload */ +#define ETH_MACCR_SARC_SHIFT (28) /* Bits 28-30: Source Address Insertion or Replacement Control */ +#define ETH_MACCR_SARC_MASK (7 << ETH_MACCR_SARC_SHIFT) +#define ETH_MACCR_ARPEN (1 << 31) /* Bit 31: ARP Offload Enable */ + +/* Ethernet MAC frame filter register */ + +#define ETH_MACPFR_PR (1 << 0) /* Bit 0: Promiscuous mode */ +#define ETH_MACPFR_HUC (1 << 1) /* Bit 1: Hash unicast */ +#define ETH_MACPFR_HMC (1 << 2) /* Bit 2: Hash multicast */ +#define ETH_MACPFR_DAIF (1 << 3) /* Bit 3: Destination address inverse filtering */ +#define ETH_MACPFR_PM (1 << 4) /* Bit 4: Pass all multicast */ +#define ETH_MACPFR_DBF (1 << 5) /* Bit 5: Disable broadcast packets */ +#define ETH_MACPFR_PCF_SHIFT (6) /* Bits 6-7: Pass control frames */ +#define ETH_MACPFR_PCF_MASK (3 << ETH_MACPFR_PCF_SHIFT) +# define ETH_MACPFR_PCF_NONE (0 << ETH_MACPFR_PCF_SHIFT) /* Prevents all control frames */ +# define ETH_MACPFR_PCF_PAUSE (1 << ETH_MACPFR_PCF_SHIFT) /* Prevents all except Pause control frames */ +# define ETH_MACPFR_PCF_ALL (2 << ETH_MACPFR_PCF_SHIFT) /* Forwards all control frames */ +# define ETH_MACPFR_PCF_FILTER (3 << ETH_MACPFR_PCF_SHIFT) /* Forwards all that pass address filter */ +#define ETH_MACPFR_SAIF (1 << 8) /* Bit 8: Source address inverse filtering */ +#define ETH_MACPFR_SAF (1 << 9) /* Bit 9: Source address filter */ +#define ETH_MACPFR_HPF (1 << 10) /* Bit 10: Hash or perfect filter */ +#define ETH_MACPFR_VTFE (1 << 16) /* Bit 16: VLAN Tag Filter Enable */ +#define ETH_MACPFR_IPFE (1 << 20) /* Bit 20: Layer 3 and Layer 4 Filter Enable */ +#define ETH_MACPFR_DNTU (1 << 21) /* Bit 21: Drop Non-TCP/UDP over IP Packets */ +#define ETH_MACPFR_RA (1 << 31) /* Bit 31: Receive all */ + +/* Ethernet MAC VLAN tag register */ + +#define ETH_MACVTR_VL_SHIFT (0) /* Bits 0-15: VLAN tag identifier (for receive frames) */ +#define ETH_MACVTR_VL_MASK (0xffff << ETH_MACVLANTR_VL_SHIFT) +# define ETH_MACVTR_VL(n) ((uint32_t)(n) << ETH_MACVLANTR_VL_SHIFT) +#define ETH_MACVLANTR_ETV (1 << 16) /* Bit 16: Enable 12-Bit VLAN Tag Comparison */ + +/* Ethernet MAC Tx Queue flow control register */ + +#define ETH_MACQTXFCR_FCB_BPA (1 << 0) /* Bit 0: Flow control busy/back pressure activate */ +#define ETH_MACQTXFCR_TFE (1 << 1) /* Bit 1: Transmit flow control enable */ +#define ETH_MACQTXFCR_PLT_SHIFT (4) /* Bits 4-6: Pause low threshold */ +#define ETH_MACQTXFCR_PLT_MASK (7 << ETH_MACQTXFCR_PLT_SHIFT) +# define ETH_MACQTXFCR_PLT_M4 (0 << ETH_MACQTXFCR_PLT_SHIFT) /* 000 PT - 4 slot times */ +# define ETH_MACQTXFCR_PLT_M28 (1 << ETH_MACQTXFCR_PLT_SHIFT) /* 001 PT - 28 slot times */ +# define ETH_MACQTXFCR_PLT_M36 (2 << ETH_MACQTXFCR_PLT_SHIFT) /* 010 PT - 36 slot times */ +# define ETH_MACQTXFCR_PLT_M144 (3 << ETH_MACQTXFCR_PLT_SHIFT) /* 011 PT - 144 slot times */ +# define ETH_MACQTXFCR_PLT_M256 (4 << ETH_MACQTXFCR_PLT_SHIFT) /* 100 PT - 256 slot times */ +# define ETH_MACQTXFCR_PLT_M512 (5 << ETH_MACQTXFCR_PLT_SHIFT) /* 101 PT - 512 slot times */ +#define ETH_MACQTXFCR_DZPQ (1 << 7) /* Bit 7: Zero-quanta pause disable */ +#define ETH_MACQTXFCR_PT_SHIFT (4) /* Bits 16-31: Pause Time */ +#define ETH_MACQTXFCR_PT_MASK (0xFFFF << ETH_MACQTXFCR_PT_SHIFT) +#define ETH_MACQTXFCR_PT(n) ((uint32_t)(n) << ETH_MACQTXFCR_PT_SHIFT) + +/* Ethernet MAC Rx flow control register */ + +#define ETH_MACRXFCR_RFE (1 << 0) /* Bit 0: Receive flow control enable */ +#define ETH_MACRXFCR_UP (1 << 1) /* Bit 1: Unicast pause frame detect */ + +/* Ethernet MAC interrupt status register */ + +#define ETH_MACISR_PHYIS (1 << 3) /* Bit 3: PHY interrupt */ +#define ETH_MACISR_PMTIS (1 << 4) /* Bit 4: PMT Interrupt Status */ +#define ETH_MACISR_LPIIS (1 << 5) /* Bit 5: LPI Interrupt Status */ +#define ETH_MACISR_MMCIS (1 << 8) /* Bit 8: MMC Interrupt Status */ +#define ETH_MACISR_MMCRXIS (1 << 9) /* Bit 9: MMC Receive Interrupt Status */ +#define ETH_MACISR_MMCTXIS (1 << 10) /* Bit 10: MMC Transmit Interrupt Status */ +#define ETH_MACISR_TSIS (1 << 12) /* Bit 12: Timestamp Interrupt Status */ +#define ETH_MACISR_TXTSIS (1 << 13) /* Bit 13: Transmit Status Interrupt */ +#define ETH_MACISR_RXSTSIS (1 << 14) /* Bit 14: Receive Status Interrupt */ + +/* Ethernet MAC interrupt mask register */ + +#define ETH_MACIER_PHYIE (1 << 3) /* Bit 3: PHY interrupt mask */ +#define ETH_MACIER_PMTIE (1 << 4) /* Bit 4: PMT Interrupt Enable */ +#define ETH_MACIER_LPIIE (1 << 5) /* Bit 5: LPI Interrupt Enable */ +#define ETH_MACIER_TSIE (1 << 12) /* Bit 12: Timestamp Interrupt Enable */ +#define ETH_MACIER_TXSTSIE (1 << 13) /* Bit 13: Transmit Status Interrupt Enable */ +#define ETH_MACIER_RXSTSIE (1 << 14) /* Bit 14: Receive Status Interrupt Enable */ +#define ETH_MACIER_ALLINTS (ETH_MACIER_PHYIE | ETH_MACIER_PMTIE | ETH_MACIER_LPIIE |\ + ETH_MACIER_TSIE | ETH_MACIER_TXSTSIE | ETH_MACIER_RXSTSIE) + +/* Ethernet MAC MDIO address register */ + +#define ETH_MACMDIOAR_MB (1 << 0) /* Bit 0: MII busy */ +#define ETH_MACMDIOAR_C45E (1 << 1) /* Bit 1: Clause 45 PHY Enable */ +#define ETH_MACMDIOAR_GOC_SHIFT (2) /* Bits 2-3: MII Operation Command */ +# define ETH_MACMDIOAR_GOC_WRITE (1 << ETH_MACMDIOAR_GOC_SHIFT) /* Write */ +# define ETH_MACMDIOAR_GOC_PRIA (2 << ETH_MACMDIOAR_GOC_SHIFT) /* Post Read Increment Address for Clause 45 PHY */ +# define ETH_MACMDIOAR_GOC_READ (3 << ETH_MACMDIOAR_GOC_SHIFT) /* Read */ +#define ETH_MACMDIOAR_SKAP (1 << 4) /* Bit 4: Skip Address Packet */ +#define ETH_MACMDIOAR_CR_SHIFT (8) /* Bits 8-11: Clock range */ +#define ETH_MACMDIOAR_CR_MASK (7 << ETH_MACMDIOAR_CR_SHIFT) +# define ETH_MACMDIOAR_CR_DIV42 (0 << ETH_MACMDIOAR_CR_SHIFT) /* 60-100 MHz HCLK/42 */ +# define ETH_MACMDIOAR_CR_DIV62 (1 << ETH_MACMDIOAR_CR_SHIFT) /* 100-150 MHz HCLK/62 */ +# define ETH_MACMDIOAR_CR_DIV16 (2 << ETH_MACMDIOAR_CR_SHIFT) /* 20-35 MHz HCLK/16 */ +# define ETH_MACMDIOAR_CR_DIV26 (3 << ETH_MACMDIOAR_CR_SHIFT) /* 35-60 MHz HCLK/26 */ +# define ETH_MACMDIOAR_CR_DIV102 (4 << ETH_MACMDIOAR_CR_SHIFT) /* 150-250 MHz HCLK/102 */ +# define ETH_MACMDIOAR_CR_DIV124 (5 << ETH_MACMDIOAR_CR_SHIFT) /* 250-300 MHz HCLK/124 */ +#define ETH_MACMDIOAR_NTC_SHIFT (12) /* Number of Training Clocks */ +#define ETH_MACMDIOAR_NTC_MASK (7 << ETH_MACMDIOAR_NTC_SHIFT) +# define ETH_MACMDIOAR_NTC(n) ((uint32_t)(n) << ETH_MACMDIOAR_NTC_SHIFT) +#define ETH_MACMDIOAR_RDA_SHIFT (16) /* Bits 16-20: MII register */ +#define ETH_MACMDIOAR_RDA_MASK (31 << ETH_MACMDIOAR_RDA_SHIFT) +# define ETH_MACMDIOAR_RDA(n) ((uint32_t)(n) << ETH_MACMDIOAR_RDA_SHIFT) +#define ETH_MACMDIOAR_PA_SHIFT (11) /* Bits 11-15: PHY address */ +#define ETH_MACMDIOAR_PA_MASK (31 << ETH_MACMDIOAR_PA_SHIFT) +# define ETH_MACMDIOAR_PA(n) ((uint32_t)(n) << ETH_MACMDIOAR_PA_SHIFT) +#define ETH_MACMDIOAR_BTB (1 << 26) /* Bit 26: Back to Back transactions */ +#define ETH_MACMDIOAR_PSE (1 << 27) /* Bit 27: Preamble Suppression Enable */ + +#define STM32_ETH_MACAxHR_MBC_SHIFT (24) /* Bits 24-29: Mask Byte Control */ +#define STM32_ETH_MACAxHR_MBC_MASK (0x3f << STM32_ETH_MACA0HR_MBC_SHIFT) +#define STM32_ETH_MACAxHR_SA (1 << 30) /* Bit 30: Source Address */ +#define STM32_ETH_MACAxHR_AE (1 << 31) /* Bit 31: Address Enable */ + +/* Ethernet MTL registers */ + +#define ETH_MTLOMR_DTXSTS (1 << 0) /* Bit 0: Drop Transmit Status */ +#define ETH_MTLOMR_CNTPRST (1 << 8) /* Bit 8: Counters Preset */ +#define ETH_MTLOMR_CNTCLR (1 << 9) /* Bit 9: Counters Reset */ + +#define ETH_MTLISR_Q0IS (1 << 0) /* Bit 0: Queue interrupt status */ + +#define ETH_MTLTXQOMR_FTQ (1 << 0) /* Bit 0: Flush Transmit Queue */ +#define ETH_MTLTXQOMR_TSF (1 << 1) /* Bit 1: Transmit Store and Forward */ +#define ETH_MTLTXQOMR_TXQEN_SHIFT (2) /* Bits 2-3: Transmit Queue Enable */ +#define ETH_MTLTXQOMR_TXQEN_MASK (0x3 << ETH_MTLTXQOMR_TXQEN_SHIFT) +#define ETH_MTLTXQOMR_TXQEN_DISABLED (0) +#define ETH_MTLTXQOMR_TXQEN_ENABLED (2) + +#define ETH_MTLTXQOMR_TTC_SHIFT (4) /* Bits 4-6: Transmit Threshold Control */ +#define ETH_MTLTXQOMR_TTC_MASK (0x7 << ETH_MTLTXQOMR_TTC_SHIFT) +# define ETH_MTLTXQOMR_TTC_32 (0x0 << ETH_MTLTXQOMR_TTC_SHIFT) +# define ETH_MTLTXQOMR_TTC_64 (0x1 << ETH_MTLTXQOMR_TTC_SHIFT) +# define ETH_MTLTXQOMR_TTC_96 (0x2 << ETH_MTLTXQOMR_TTC_SHIFT) +# define ETH_MTLTXQOMR_TTC_128 (0x3 << ETH_MTLTXQOMR_TTC_SHIFT) +# define ETH_MTLTXQOMR_TTC_192 (0x4 << ETH_MTLTXQOMR_TTC_SHIFT) +# define ETH_MTLTXQOMR_TTC_256 (0x5 << ETH_MTLTXQOMR_TTC_SHIFT) +# define ETH_MTLTXQOMR_TTC_364 (0x6 << ETH_MTLTXQOMR_TTC_SHIFT) +# define ETH_MTLTXQOMR_TTC_512 (0x7 << ETH_MTLTXQOMR_TTC_SHIFT) +#define ETH_MTLTXQOMR_TQS_SHIFT (16) /* Bits 16-24: Transmit Queue Size */ +#define ETH_MTLTXQOMR_TQS_MASK (0x1ff << ETH_MTLTXQOMR_TQS_SHIFT) + +#define ETH_MTLTXQUR_UFFRMCNT_SHIFT (0) /* Underflow Packet Counter */ +#define ETH_MTLTXQUR_UFFRMCNT_MASK (0x7ff << ETH_MTLTXQUR_UFFRMCNT_SHIFT) +#define ETH_MTLTXQUR_UFCNTOVF (1 << 11) + +#define ETH_MTLTXQDR_TXQPAUSED (1 << 0) /* Transmit Queue in Pause */ +#define ETH_MTLTXQDR_TRCSTS_SHIFT (1) /* MTL Tx Queue Read Controller Status */ +#define ETH_MTLTXQDR_TRCSTS_MASK (0x3 << ETH_MTLTXQDR_TRCSTS_SHIFT) +#define ETH_MTLTXQDR_TWCSTS (1 << 3) /* Bit 3: MTL Tx Queue Write Controller Status */ +#define ETH_MTLTXQDR_TXQSTS (1 << 4) /* Bit 4: MTL Tx Queue Not Empty Status */ +#define ETH_MTLTXQDR_TXSTSFSTS (1 << 5) /* Bit 5: MTL Tx Status FIFO Full Status */ +#define ETH_MTLTXQDR_PTXQ_SHIFT (16) /* Bits 16-18: Number of Packets in the Transmit Queue */ +#define ETH_MTLTXQDR_PTXQ_MASK (0x7 << ETH_MTLTXQDR_PTXQ_SHIFT) +#define ETH_MTLTXQDR_STXSTSF_SHIFT (20) /* Bits 20-22: Number of Status Words in Tx Status FIFO of Queue */ +#define ETH_MTLTXQDR_STXSTSF_MASK (0x7 << ETH_MTLTXQDR_STXSTSF_SHIFT) + +#define ETH_MTLQICSR_TXUNFIS (1 << 0) /* Bit 0: Transmit Queue Underflow Interrupt Status */ +#define ETH_MTLQICSR_TXUIE (1 << 8) /* Bit 8: Transmit Queue Underflow Interrupt Enable */ +#define ETH_MTLQICSR_RXOVFIS (1 << 16) /* Bit 16: Receive Queue Overflow Interrupt Status */ +#define ETH_MTLQICSR_RXOIE (1 << 24) /* Bit 24: Receive Queue Overflow Interrupt Enable */ + +#define ETH_MTLRXQOMR_RTC_SHIFT (0) /* Bits 0-1: Receive Queue Threshold Control */ +#define ETH_MTLRXQOMR_RTC_MASK (0x3 << ETH_MTLRXQOMR_RTC_SHIFT) +# define ETH_MTLRXQOMR_RTC_64 (0x0 << ETH_MTLRXQOMR_RTC_SHIFT) +# define ETH_MTLRXQOMR_RTC_32 (0x1 << ETH_MTLRXQOMR_RTC_SHIFT) +# define ETH_MTLRXQOMR_RTC_96 (0x2 << ETH_MTLRXQOMR_RTC_SHIFT) +# define ETH_MTLRXQOMR_RTC_128 (0x3 << ETH_MTLRXQOMR_RTC_SHIFT) +#define ETH_MTLRXQOMR_FUP (1 << 3) /* Bit 3: Forward Undersized Good Packets */ +#define ETH_MTLRXQOMR_FEP (1 << 4) /* Bit 4: Forward Error Packets */ +#define ETH_MTLRXQOMR_RSF (1 << 5) /* Bit 5: Receive Queue Store and Forward */ +#define ETH_MTLRXQOMR_DIS_TCP_EF (1 << 6) /* Bit 6: Disable Dropping of TCP/IP Checksum Error Packets */ +#define ETH_MTLRXQOMR_EHFC (1 << 7) /* Bit 7: Enable Hardware Flow Control */ +#define ETH_MTLRXQOMR_RFA_SHIFT (8) /* Bits 8-10: Threshold for Activating Flow Control */ +#define ETH_MTLRXQOMR_RFA_MASK (0x7 << ETH_MTLRXQOMR_RFA_SHIFT) +#define ETH_MTLRXQOMR_RFD_SHIFT (14) /* Bits 14-16: Threshold for Deactivating Flow Control */ +#define ETH_MTLRXQOMR_RFD_MASK (0x7 << ETH_MTLRXQOMR_RFD_SHIFT) +#define ETH_MTLRXQOMR_RQS_SHIFT (20) /* Bits 20-22: Receive Queue Size */ +#define ETH_MTLRXQOMR_RQS_MASK (0x7 << ETH_MTLRXQOMR_RQS_SHIFT) + +#define ETH_MTLRXQMPOCR_OVFPKTCNT_SHIFT (0) /* Bits 0-10: Overflow Packet Counter */ +#define ETH_MTLRXQMPOCR_OVFPKTCNT_MASK (0x7ff << ETH_MTLRXQMPOCR_OVFPKTCNT_SHIFT) +#define ETH_MTLRXQMPOCR_OVFCNTOVF (1 << 11) /* Bit 11: Overflow Counter Overflow Bit */ +#define ETH_MTLRXQMPOCR_MISPKTCNT_SHIFT (16) /* Bits 16-26: Missed Packet Counter */ +#define ETH_MTLRXQMPOCR_MISPKTCNT_MASK (0x7ff << ETH_MTLRXQMPOCR_MISPKTCNT_SHIFT) +#define ETH_MTLRXQMPOCR_MISCNTOVF (1 << 27) /* Bit 27: Missed Packet Counter Overflow Bit */ + +#define ETH_MTLRXQDR_RWCSTS (1 << 0) /* Bit 0: MTL Rx Queue Write Controller Active Status */ +#define ETH_MTLRXQDR_RRCSTS_SHIFT (1) /* Bits 1-2: MTL Rx Queue Read Controller State */ +#define ETH_MTLRXQDR_RRCSTS_MASK (0x3 << ETH_MTLRXQDR_RRCSTS_SHIFT) +#define ETH_MTLRXQDR_RXQSTS_SHIFT (4) /* Bits 4-5: MTL Rx Queue Fill-Level Status */ +#define ETH_MTLRXQDR_RXQSTS_MASK (0x3 << ETH_MTLRXQDR_RXQSTS_SHIFT) +#define ETH_MTLRXQDR_PRXQ_SHIFT (16) /* Bits 16-29: Number of Packets in Receive Queue */ +#define ETH_MTLRXQDR_PRXQ_MASK (0x3fff << ETH_MTLRXQDR_PRXQ_SHIFT) + +/* Ethernet MAC MDIO data register */ + +/* Ethernet DMA registers ***************************************************************************/ + +#define ETH_DMAMR_SWR (1 << 0) /* Bit 0: Software Reset */ +#define ETH_DMAMR_DA (1 << 1) /* Bit 1: DMA Tx or Rx Arbitration Scheme */ +#define ETH_DMAMR_TXPR (1 << 11) /* Bit 11: Transmit priority */ +#define ETH_DMAMR_PR_SHIFT (12) /* Bits 12-14: Priority ratio */ +#define ETH_DMAMR_PR_MASK (0x7 << ETH_DMAMR_PR_SHIFT) +# define ETH_DMAMR_PR_1TO1 (0x0 << ETH_DMAMR_PR_SHIFT) +# define ETH_DMAMR_PR_2TO1 (0x1 << ETH_DMAMR_PR_SHIFT) +# define ETH_DMAMR_PR_3TO1 (0x2 << ETH_DMAMR_PR_SHIFT) +# define ETH_DMAMR_PR_4TO1 (0x3 << ETH_DMAMR_PR_SHIFT) +# define ETH_DMAMR_PR_5TO1 (0x4 << ETH_DMAMR_PR_SHIFT) +# define ETH_DMAMR_PR_6TO1 (0x5 << ETH_DMAMR_PR_SHIFT) +# define ETH_DMAMR_PR_7TO1 (0x6 << ETH_DMAMR_PR_SHIFT) +# define ETH_DMAMR_PR_8TO1 (0x7 << ETH_DMAMR_PR_SHIFT) + +#define ETH_DMAMR_INTM_SHIFT (16) /* Bits 16-17: Interrupt Mode */ +#define ETH_DMAMR_INTM_MASK (0x3 << ETH_DMAMR_INTM_SHIFT) + +#define STM32_ETH_DMASBMR_FB (1 << 0) /* Bit 0: Fixed Burst Length */ +#define STM32_ETH_DMASBMR_AAL (1 << 12) /* Bit 12: Address-Aligned Beats */ +#define STM32_ETH_DMASBMR_MB (1 << 14) /* Bit 14: Mixed Burst */ +#define STM32_ETH_DMASBMR_RB (1 << 15) /* Bit 15: Rebuild INCRx Burst */ + +#define ETH_DMACIER_TIE (1 << 0) /* Bit 0: Transmit interrupt enable */ +#define ETH_DMACIER_TXSE (1 << 1) /* Bit 1: Transmit process stopped interrupt enable */ +#define ETH_DMACIER_TBUE (1 << 2) /* Bit 2: Transmit buffer unavailable interrupt enable */ + +#define ETH_DMACIER_RIE (1 << 6) /* Bit 6: Receive interrupt enable */ +#define ETH_DMACIER_RBUE (1 << 7) /* Bit 7: Receive buffer unavailable interrupt enable */ +#define ETH_DMACIER_RSE (1 << 8) /* Bit 8: Receive process stopped interrupt enable */ +#define ETH_DMACIER_RWTE (1 << 9) /* Bit 9: Receive watchdog timeout interrupt enable */ +#define ETH_DMACIER_ETIE (1 << 10) /* Bit 10: Early transmit interrupt enable */ +#define ETH_DMACIER_ERIE (1 << 11) /* Bit 11: Early receive interrupt enable*/ +#define ETH_DMACIER_FBEE (1 << 12) /* Bit 12: Fatal bus error interrupt enable*/ +#define ETH_DMACIER_CDEE (1 << 13) /* Bit 13: Context descriptor error interrupt enable */ +#define ETH_DMACIER_AIE (1 << 14) /* Bit 14: Abnormal interrupt summary interrupt enable */ +#define ETH_DMACIER_NIE (1 << 15) /* Bit 15: Normal interrupt summary enable */ + +#define ETH_DMACSR_TI (1 << 0) /* Bit 0: Transmit interrupt */ +#define ETH_DMACSR_TPS (1 << 1) /* Bit 1: Transmit process stopped interrupt */ +#define ETH_DMACSR_TBU (1 << 2) /* Bit 2: Transmit buffer unavailable interrupt */ + +#define ETH_DMACSR_RI (1 << 6) /* Bit 6: Receive interrupt */ +#define ETH_DMACSR_RBU (1 << 7) /* Bit 7: Receive buffer unavailable interrupt */ +#define ETH_DMACSR_RPS (1 << 8) /* Bit 8: Receive process stopped interrupt */ +#define ETH_DMACSR_RWT (1 << 9) /* Bit 9: Receive watchdog timeout interrupt */ +#define ETH_DMACSR_ETI (1 << 10) /* Bit 10: Early transmit interrupt */ +#define ETH_DMACSR_ERI (1 << 11) /* Bit 11: Early receive interrupt */ +#define ETH_DMACSR_FBE (1 << 12) /* Bit 12: Fatal bus error interrupt */ +#define ETH_DMACSR_CDE (1 << 13) /* Bit 13: Context descriptor error interrupt */ +#define ETH_DMACSR_AIS (1 << 14) /* Bit 14: Abnormal interrupt summary interrupt */ +#define ETH_DMACSR_NIS (1 << 15) /* Bit 15: Normal interrupt summary */ +#define ETH_DMACSR_TEB_SHIFT (16) /* Bits 16-18: Tx DMA error bits */ +#define ETH_DMACSR_TEB_MASK (0x7 << ETH_DMACSR_TEB_SHIFT) +#define ETH_DMACSR_REB_SHIFT (19) /* Bits 19-21: Rx DMA error bits */ +#define ETH_DMACSR_REB_MASK (0x7 << ETH_DMACSR_REB_SHIFT) + +#define ETH_DMACTXDTPR_TDT_SHIFT (2) /* Transmit Descriptor Tail Pointer */ +#define ETH_DMACTXDTPR_TDT_MASK (0x3fffffff << ETH_DMACTXDTPR_TDT_SHIFT) + +#define ETH_DMACCR_MSS_SHIFT (0) /* Bits 0-13: Maximum Segment Size */ +#define ETH_DMACCR_MSS_MASK (0x3fff << ETH_DMACCR_MSS_SHIFT) +#define ETH_DMACCR_PBLX8 (1 << 16) /* Bit 16: 8xPBL mode */ +#define ETH_DMACCR_DSL_SHIFT (18) /* Bits 18-20: Descriptor Skip Length */ +#define ETH_DMACCR_DSL_MASK (0x7 << ETH_DMACCR_DSL_SHIFT) +#define ETH_DMACCR_DSL(n) ((n) << ETH_DMACCR_DSL_SHIFT) + +#define ETH_DMACTXCR_ST (1 << 0) /* Bit 0: Start or Stop Transmission Command */ +#define ETH_DMACTXCR_OSF (1 << 4) /* Bit 4: Operate on Second Packet */ +#define ETH_DMACTXCR_TSE (1 << 12) /* Bit 12: TCP Segmentation Enabled */ +#define ETH_DMACTXCR_TXPBL_SHIFT (16) /* Bits 16-21: Transmit Programmable Burst Length */ +#define ETH_DMACTXCR_TXPBL_MASK (0x3f << ETH_DMACTXCR_TXPBL_SHIFT) +#define ETH_DMACTXCR_TXPBL(n) ((n) << ETH_DMACTXCR_TXPBL_SHIFT) + +#define ETH_DMACRXCR_SR (1 << 0) /* Bit0: Start or Stop Receive */ +#define ETH_DMACRXCR_RBSZ_SHIFT (1) /* Bits 1-14: Receive Buffer size */ +#define ETH_DMACRXCR_RBSZ_MASK (0x3fff << ETH_DMACRXCR_RBSZ_SHIFT) +#define ETH_DMACRXCR_RBSZ(n) ((n) << ETH_DMACRXCR_RBSZ_SHIFT) +#define ETH_DMACRXCR_RXPBL_SHIFT (16) /* Bits 16-21: Receive Programmable Burst Length */ +#define ETH_DMACRXCR_RXPBL_MASK (0x3f << ETH_DMACRXCR_RXPBL_SHIFT) +#define ETH_DMACRXCR_RXPBL(n) ((n) << ETH_DMACRXCR_RXPBL_SHIFT) +#define ETH_DMACRXCR_RPF (1 << 31) /* Bit 31: DMA Rx Channel Packet Flush */ + +/* DMA Descriptors **********************************************************************************/ + +/* TDES0: TDES0 normal descriptor, read-format (header or buf 1 address[31:0]) + * TDES1: TDES1 normal descriptor, read-format (buf2 address[31:0] or buf1 address[63:32] + * TDES2: TDES2 normal descriptor, read-format + */ + +#define ETH_TDES2_RD_HL_BL1_SHIFT (0) /* Header Length or Buffer 1 Length */ +#define ETH_TDES2_RD_HL_BL1_MASK (0x3fff << ETH_TDES2_RD_HL_BL1_SHIFT) +#define ETH_TDES2_RD_VTIR_SHIFT (14) /* VLAN Tag Insertion or Replacement */ +#define ETH_TDES2_RD_VTIR_MASK (0x3 << ETH_TDES2_RD_VTIR_SHIFT) +#define ETH_TDES2_RD_B2L_SHIFT (16) /* Buffer 2 Length */ +#define ETH_TDES2_RD_B2L_MASK (0x3fff << ETH_TDES2_RD_B2L_SHIFT) +#define ETH_TDES2_RD_TTSE (1 << 30) /* Bit 30: Transmit Timestamp Enable */ +#define ETH_TDES2_RD_IOC (1 << 31) /* Bit 31: Interrupt on Completion */ + +/* TDES3: TDES3 normal descriptor, read-format */ + +#define ETH_TDES3_RD_FL_TPL_SHIFT (0) /* Packet Length or TCP Payload Length */ +#define ETH_TDES3_RD_FL_TPL_MASK (0x7fff << ETH_TDES3_RD_FL_TPL_SHIFT) +#define ETH_TDES3_RD_TPL (1 << 15) /* Bit 15: Reserved or TCP Payload Length */ +#define ETH_TDES3_RD_CIC_TPL_SHIFT (16) /* Checksum Insertion Control or TCP Payload Length */ +#define ETH_TDES3_RD_CIC_TPL_MASK (0x3 << ETH_TDES3_RD_CIC_TPL_SHIFT) +#define ETH_TDES3_RD_TSE (1 << 18) /* Bit 18: TCP Segmentation Enable */ +#define ETH_TDES3_RD_THL_SHIFT (19) /* TCP Header Length */ +#define ETH_TDES3_RD_THL_MASK (0xf << ETH_TDES3_RD_THL_SHIFT) +#define ETH_TDES3_RD_SAIC_SHIFT (23) /* SA Insertion Control */ +#define ETH_TDES3_RD_SAIC_MASK (0x7 << ETH_TDES3_RD_SAIC_SHIFT) +#define ETH_TDES3_RD_CPC_SHIFT (26) /* CRC Pad Control */ +#define ETH_TDES3_RD_CPC_MASK (0x3 << ETH_TDES3_RD_CPC_SHIFT) +#define ETH_TDES3_RD_LD (1 << 28) /* Bit 28: Last Descriptor */ +#define ETH_TDES3_RD_FD (1 << 29) /* Bit 29: First Descriptor */ +#define ETH_TDES3_RD_CTXT (1 << 30) /* Bit 30: Context Type */ +#define ETH_TDES3_RD_OWN (1 << 31) /* Bit 31: Own bit */ + +/* TDES0: TDES0 normal descriptor, write-back format (time stamp low) + * TDES1: TDES1 normal descriptor, write-back format (time stamp high) + * TDES2: TDES2 normal descriptor, write-back format (reserved) + * TDES3: TDES3 normal descriptor, write-back format + */ + +#define ETH_TDES3_WB_IHE (1 << 0) /* Bit 0: IP Header Error */ +#define ETH_TDES3_WB_DB (1 << 1) /* Bit 1: Deferred bit */ +#define ETH_TDES3_WB_UF (1 << 2) /* Bit 1: Underflow error */ +#define ETH_TDES3_WB_ED (1 << 3) /* Bit 2: Excessive deferral */ +#define ETH_TDES3_WB_CC_SHIFT (4) /* Bits 4-7: Collision count */ +#define ETH_TDES3_WB_CC_MASK (15 << ETH_TDES3_CC_WB_SHIFT) +#define ETH_TDES3_WB_EC (1 << 8) /* Bit 8: Excessive collision */ +#define ETH_TDES3_WB_LC (1 << 9) /* Bit 9: Late collision */ +#define ETH_TDES3_WB_NC (1 << 10) /* Bit 10: No carrier */ +#define ETH_TDES3_WB_LOC (1 << 11) /* Bit 11: Loss of carrier */ +#define ETH_TDES3_WB_PCE (1 << 12) /* Bit 12: Payload checksum error */ +#define ETH_TDES3_WB_FF (1 << 13) /* Bit 13: Packet flushed */ +#define ETH_TDES3_WB_JT (1 << 14) /* Bit 14: Jabber timeout */ +#define ETH_TDES3_WB_ES (1 << 15) /* Bit 15: Error summary */ +#define ETH_TDES3_WB_TTSS (1 << 17) /* Bit 17: Transmit time stamp status */ +#define ETH_TDES3_WB_LD (1 << 28) /* Bit 28: Last Descriptor */ +#define ETH_TDES3_WB_FD (1 << 29) /* Bit 29: First Descriptor */ +#define ETH_TDES3_WB_CTXT (1 << 30) /* Bit 30: Context Type */ +#define ETH_TDES0_WB_OWN (1 << 31) /* Bit 31: Own bit */ + +/* TDES0: TDES0 context descriptor (time stamp low) + * TDES1: TDES1 context descriptor (time stamp high) + * TDES2: TDES2 context descriptor + */ + +#define ETH_CTX_TDES2_MSS_SHIFT (0) /* Maximum Segment Size */ +#define ETH_CTX_TDES2_MSS_MASK (0x3fff << ETH_CTX_TDES2_MSS_SHIFT) +#define ETH_CTX_TDES2_IVT_SHIFT (16) /* Inner VLAN Tag */ +#define ETH_CTX_TDES2_IVT_MASK (0xffff << ETH_CTX_TDES2_IVT_SHIFT) + +/* TDES3: TDES3 context descriptor */ + +#define ETH_CTX_TDES3_VT_SHIFT (0) /* VLAN Tag */ +#define ETH_CTX_TDES3_VT_MASK (0xffff << ETH_CTX_TDES3_VT_SHIFT) +#define ETH_CTX_TDES3_VLTV (1 << 16) /* Bit 16: VLAN Tag Valid */ +#define ETH_CTX_TDES3_IVLTV (1 << 17) /* Bit 17: Inner VLAN Tag Valid */ +#define ETH_CTX_TDES3_IVTIR_SHIFT (18) /* Inner VLAN Tag Insert or Replace */ +#define ETH_CTX_TDES3_IVTIR_MASK (0x3 << ETH_CTX_TDES3_IVTIR_SHIFT) +#define ETH_CTX_TDES3_CDE (1 << 23) /* Bit 23: Context Descriptor Error */ +#define ETH_CTX_TDES3_TCMSSV (1 << 26) /* Bit 26: One-Step Timestamp Correction Input or MSS Valid */ +#define ETH_CTX_TDES3_OSTC (1 << 27) /* Bit 27: One-Step Timestamp Correction Enable */ +#define ETH_CTX_TDES3_CTXT (1 << 30) /* Bit 30: Context Type */ +#define ETH_CTX_TDES3_OWN (1 << 31) /* Bit 31: Own bit */ + +/* RDES0: Receive descriptor Word0, read-format (Buffer 1 address[31:0]) + * RDES1: Receive descriptor Word1, read-format (Reserved or Header/Buffer 1 Address[63:32] address) + * RDES2: Receive descriptor Word2, read-format (Payload or Buffer 2 Address[31:0]) + * RDES3: Receive descriptor Word3, read-format + */ + +#define ETH_RDES3_RD_BUF1V (1 << 24) /* Bit 24: Buffer 1 Address Valid */ +#define ETH_RDES3_RD_BUF2V (1 << 25) /* Bit 25: Buffer 2 Address Valid */ +#define ETH_RDES3_RD_IOC (1 << 30) /* Bit 30: Interrupt Enabled on Completion */ +#define ETH_RDES3_RD_OWN (1 << 31) /* Bit 31: Own bit */ + +/* RDES0: Receive descriptor Word0, write-back format */ + +#define ETH_RDES0_WB_OVT_SHIFT (0) /* Bits 0-15: Outer VLAN Tag */ +#define ETH_RDES0_WB_OVT_MASK (0xffff << ETH_RDES0_WB_OVT_SHIFT) +#define ETH_RDES0_WB_IVT_SHIFT (16) /* Bits 16-31: Inner VLAN Tag */ +#define ETH_RDES0_WB_IVT_MASK (0xffff << ETH_RDES0_WB_IVT_SHIFT) + +/* RDES1: Receive descriptor Word1, write-back format */ + +#define ETH_RDES1_WB_PT_SHIFT (0) /* Bits 0-2: Payload Type */ +#define ETH_RDES1_WB_PT_MASK (0x7 << ETH_RDES1_WB_PT_SHIFT) +# define ETH_RDES1_WB_PT_UNKNOWN (0x0) +# define ETH_RDES1_WB_PT_UDP (0x1) +# define ETH_RDES1_WB_PT_TCP (0x2) +# define ETH_RDES1_WB_PT_ICMP (0x3) +#define ETH_RDES1_WB_IPHE (1 << 3) /* Bit 3: IP Header Error */ +#define ETH_RDES1_WB_IPV4 (1 << 4) /* Bit 4: IPv4 Header Present */ +#define ETH_RDES1_WB_IPV6 (1 << 5) /* Bit 5: IPv6 Header Present */ +#define ETH_RDES1_WB_IPCB (1 << 6) /* Bit 6: IP Checksum Bypassed */ +#define ETH_RDES1_WB_IPCE (1 << 7) /* Bit 7: IP Payload Error */ +#define ETH_RDES1_WB_PMT_SHIFT (8) /* Bits 8-11: PTP Message Type */ +#define ETH_RDES1_WB_PMT_MASK (0xf << ETH_RDES1_WB_PMT_SHIFT) +#define ETH_RDES1_WB_PFT (1 << 12) /* Bit 12: PTP Packet Type */ +#define ETH_RDES1_WB_PV (1 << 13) /* Bit 13: PTP Version */ +#define ETH_RDES1_WB_TSA (1 << 14) /* Bit 14: Timestamp Available */ +#define ETH_RDES1_WB_TD (1 << 15) /* Bit 15: Timestamp Dropped */ +#define ETH_RDES1_WB_OPC_SHIFT (16) /* Bits 16-31: OAM Sub-Type Code, or MAC Control Packet opcode */ +#define ETH_RDES1_WB_OPC_MASK (0xffff << ETH_RDES1_WB_OPC_SHIFT) + +/* RDES2: Receive descriptor Word2, write-back format */ + +#define ETH_RDES2_WB_ARPNR (1 << 10) /* Bit 10: ARP Reply Not Generated */ +#define ETH_RDES2_WB_VF (1 << 15) /* Bit 15: VLAN Filter Status */ +#define ETH_RDES2_WB_SAF (1 << 16) /* Bit 16: SA Address Filter Fail */ +#define ETH_RDES2_WB_DAF (1 << 17) /* Bit 17: Destination Address Filter Fail */ +#define ETH_RDES2_WB_HF (1 << 18) /* Bit 18: Hash Filter Status */ +#define ETH_RDES2_WB_MADRM_SHIFT (19) /* Bits 19-26: MAC Address Match or Hash Value */ +#define ETH_RDES2_WB_MADRM_MASK (0xff << ETH_RDES1_WB_MADRM_SHIFT) +#define ETH_RDES2_WB_L3FM (1 << 27) /* Bit 27: Layer 3 Filter Match */ +#define ETH_RDES2_WB_L4FM (1 << 28) /* Bit 28: Layer 4 Filter Match */ +#define ETH_RDES2_WB_L3L4FM_SHIFT (29) /* Bits 29-31: Layer 3 and Layer 4 Filter Number Matched */ +#define ETH_RDES2_WB_L3L4FM_MASK (0x7 << ETH_RDES1_WB_L3L4FM_SHIFT) + +/* RDES3: Receive descriptor Word3, write-back format */ + +#define ETH_RDES3_WB_PL_SHIFT (0) /* Bits 0-14: Packet Length */ +#define ETH_RDES3_WB_PL_MASK (0x7fff << ETH_RDES3_WB_PL_SHIFT) +#define ETH_RDES3_WB_ES (1 << 15) /* Bit 15: Error Summary */ +#define ETH_RDES3_WB_LT_SHIFT (16) /* Bits 16-18: Length/Type Field */ +#define ETH_RDES3_WB_LT_MASK (0x7 << ETH_RDES3_WB_LT_SHIFT) +#define ETH_RDES3_WB_DE (1 << 19) /* Bit 19: Dribble Bit Error */ +#define ETH_RDES3_WB_RE (1 << 20) /* Bit 20: Receive Error */ +#define ETH_RDES3_WB_OE (1 << 21) /* Bit 21: Overflow Error */ +#define ETH_RDES3_WB_RWT (1 << 22) /* Bit 22: Receive Watchdog Timeout */ +#define ETH_RDES3_WB_GP (1 << 23) /* Bit 23: Giant Packet */ +#define ETH_RDES3_WB_CE (1 << 24) /* Bit 24: CRC Error */ +#define ETH_RDES3_WB_RS0V (1 << 25) /* Bit 25: Receive Status RDES0 Valid */ +#define ETH_RDES3_WB_RS1V (1 << 26) /* Bit 26: Receive Status RDES1 Valid */ +#define ETH_RDES3_WB_RS2V (1 << 27) /* Bit 27: Receive Status RDES2 Valid */ +#define ETH_RDES3_WB_LD (1 << 28) /* Bit 28: Last Descriptor */ +#define ETH_RDES3_WB_FD (1 << 29) /* Bit 29: First Descriptor */ +#define ETH_RDES3_WB_CTXT (1 << 30) /* Bit 30: Context Type */ +#define ETH_RDES3_WB_OWN (1 << 31) /* Bit 31: Own bit */ + +/* RDES0: RDES0 context descriptor (time stamp low) + * RDES1: RDES1 context descriptor (time stamp high) + * RDES2: RDES2 context descriptor (reserved) + * RDES3: RDES3 context descriptor + */ + +#define ETH_CTX_RDES3_CTXT (1 << 30) /* Bit 30: Context Type */ +#define ETH_CTX_RDES3_OWN (1 << 31) /* Bit 31: Own bit */ + +/**************************************************************************************************** + * Public Types + ****************************************************************************************************/ + +#ifndef __ASSEMBLY__ + +/* Ethernet RX/TX DMA Descriptor */ + +struct eth_desc_s +{ + /* DMA descriptor words */ + + volatile uint32_t des0; + volatile uint32_t des1; + volatile uint32_t des2; + volatile uint32_t des3; +}; + +/**************************************************************************************************** + * Public Functions + ****************************************************************************************************/ + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_STM32H7_STM32H7X3XX */ +#endif /* __ARCH_ARM_SRC_STM32H7_CHIP_STM32_ETHERNET_H */ diff --git a/arch/arm/src/stm32h7/stm32_ethernet.c b/arch/arm/src/stm32h7/stm32_ethernet.c new file mode 100644 index 00000000000..eb3b0d5559d --- /dev/null +++ b/arch/arm/src/stm32h7/stm32_ethernet.c @@ -0,0 +1,4410 @@ +/**************************************************************************** + * arch/arm/src/stm32h7/stm32_ethernet.c + * + * Copyright (C) 2015-2017 Gregory Nutt. All rights reserved. + * + * Author: Gregory Nutt + * Author: Jukka Laitinen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_NET_PKT) +# include +#endif + +#include +#include "up_internal.h" +#include "barriers.h" + +#include "chip/stm32_syscfg.h" +#include "chip/stm32_pinmap.h" +#include "stm32_gpio.h" +#include "stm32_rcc.h" +#include "stm32_ethernet.h" + +#include + +/* STM32H7_NETHERNET determines the number of physical interfaces that can + * be supported by the hardware. CONFIG_STM32H7_ETHMAC will defined if + * any STM32H7 Ethernet support is enabled in the configuration. + */ + +#if STM32H7_NETHERNET > 0 && defined(CONFIG_STM32H7_ETHMAC) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Memory synchronization */ + +#define MEMORY_SYNC() do { ARM_DSB(); ARM_ISB(); } while (0) + +/* Configuration ************************************************************/ + +/* See configs/stm3240g-eval/README.txt for an explanation of the + * configuration settings. + */ + +#if STM32H7_NETHERNET > 1 +# error "Logic to support multiple Ethernet interfaces is incomplete" +#endif + +/* 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 */ + +# if defined(CONFIG_STM32H7_ETHMAC_HPWORK) +# define ETHWORK HPWORK +# elif defined(CONFIG_STM32H7_ETHMAC_LPWORK) +# define ETHWORK LPWORK +# else +# define ETHWORK LPWORK +# endif +#endif + +#ifndef CONFIG_STM32H7_PHYADDR +# error "CONFIG_STM32H7_PHYADDR must be defined in the NuttX configuration" +#endif + +#if !defined(CONFIG_STM32H7_MII) && !defined(CONFIG_STM32H7_RMII) +# warning "Neither CONFIG_STM32H7_MII nor CONFIG_STM32H7_RMII defined" +#endif + +#if defined(CONFIG_STM32H7_MII) && defined(CONFIG_STM32H7_RMII) +# error "Both CONFIG_STM32H7_MII and CONFIG_STM32H7_RMII defined" +#endif + +#ifdef CONFIG_STM32H7_MII +# if !defined(CONFIG_STM32H7_MII_MCO1) && !defined(CONFIG_STM32H7_MII_MCO2) && \ + !defined(CONFIG_STM32H7_MII_EXTCLK) +# warning "Neither CONFIG_STM32H7_MII_MCO1, CONFIG_STM32H7_MII_MCO2, nor CONFIG_STM32H7_MII_EXTCLK defined" +# endif +# if defined(CONFIG_STM32H7_MII_MCO1) && defined(CONFIG_STM32H7_MII_MCO2) +# error "Both CONFIG_STM32H7_MII_MCO1 and CONFIG_STM32H7_MII_MCO2 defined" +# endif +#endif + +#ifdef CONFIG_STM32H7_RMII +# if !defined(CONFIG_STM32H7_RMII_MCO1) && !defined(CONFIG_STM32H7_RMII_MCO2) && \ + !defined(CONFIG_STM32H7_RMII_EXTCLK) +# warning "Neither CONFIG_STM32H7_RMII_MCO1, CONFIG_STM32H7_RMII_MCO2, nor CONFIG_STM32H7_RMII_EXTCLK defined" +# endif +# if defined(CONFIG_STM32H7_RMII_MCO1) && defined(CONFIG_STM32H7_RMII_MCO2) +# error "Both CONFIG_STM32H7_RMII_MCO1 and CONFIG_STM32H7_RMII_MCO2 defined" +# endif +#endif + +#ifdef CONFIG_STM32H7_AUTONEG +# ifndef CONFIG_STM32H7_PHYSR +# error "CONFIG_STM32H7_PHYSR must be defined in the NuttX configuration" +# endif +# ifdef CONFIG_STM32H7_PHYSR_ALTCONFIG +# ifndef CONFIG_STM32H7_PHYSR_ALTMODE +# error "CONFIG_STM32H7_PHYSR_ALTMODE must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H7_PHYSR_10HD +# error "CONFIG_STM32H7_PHYSR_10HD must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H7_PHYSR_100HD +# error "CONFIG_STM32H7_PHYSR_100HD must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H7_PHYSR_10FD +# error "CONFIG_STM32H7_PHYSR_10FD must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H7_PHYSR_100FD +# error "CONFIG_STM32H7_PHYSR_100FD must be defined in the NuttX configuration" +# endif +# else +# ifndef CONFIG_STM32H7_PHYSR_SPEED +# error "CONFIG_STM32H7_PHYSR_SPEED must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H7_PHYSR_100MBPS +# error "CONFIG_STM32H7_PHYSR_100MBPS must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H7_PHYSR_MODE +# error "CONFIG_STM32H7_PHYSR_MODE must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H7_PHYSR_FULLDUPLEX +# error "CONFIG_STM32H7_PHYSR_FULLDUPLEX must be defined in the NuttX configuration" +# endif +# endif +#endif + +#ifdef CONFIG_STM32H7_ETH_PTP +# warning "CONFIG_STM32H7_ETH_PTP is not yet supported" +#endif + +#undef CONFIG_STM32H7_ETH_HWCHECKSUM + +/* Add 4 to the configured buffer size to account for the 2 byte checksum + * memory needed at the end of the maximum size packet. Buffer sizes must + * be an even multiple of 4, 8, or 16 bytes (depending on buswidth). We + * will use the 16-byte alignment in all cases. + */ + +#define OPTIMAL_ETH_BUFSIZE ((CONFIG_NET_ETH_PKTSIZE + 4 + 15) & ~15) + +#ifdef CONFIG_STM32H7_ETH_BUFSIZE +# define ETH_BUFSIZE CONFIG_STM32H7_ETH_BUFSIZE +#else +# define ETH_BUFSIZE OPTIMAL_ETH_BUFSIZE +#endif + +#if ETH_BUFSIZE > ETH_CTX_TDES2_MSS_MASK +# error "ETH_BUFSIZE is too large" +#endif + +#if (ETH_BUFSIZE & 15) != 0 +# error "ETH_BUFSIZE must be aligned" +#endif + +#if ETH_BUFSIZE != OPTIMAL_ETH_BUFSIZE +# warning "You using an incomplete/untested configuration" +#endif + +#ifndef CONFIG_STM32H7_ETH_NRXDESC +# define CONFIG_STM32H7_ETH_NRXDESC 8 +#endif +#ifndef CONFIG_STM32H7_ETH_NTXDESC +# define CONFIG_STM32H7_ETH_NTXDESC 4 +#endif + +/* We need at least one more free buffer than transmit buffers */ + +#define STM32_ETH_NFREEBUFFERS (CONFIG_STM32H7_ETH_NTXDESC+1) + +/* Buffers use fro DMA access must begin on an address aligned with the + * D-Cache line and must be an even multiple of the D-Cache line size. + * These size/alignment requirements are necessary so that D-Cache flush + * and invalidate operations will not have any additional effects. + * + * The TX and RX descriptors are 16 bytes in size + */ + +#define DMA_BUFFER_MASK (ARMV7M_DCACHE_LINESIZE - 1) +#define DMA_ALIGN_UP(n) (((n) + DMA_BUFFER_MASK) & ~DMA_BUFFER_MASK) +#define DMA_ALIGN_DOWN(n) ((n) & ~DMA_BUFFER_MASK) + +#define DESC_SIZE 16 +#define DESC_PADSIZE DMA_ALIGN_UP(DESC_SIZE) +#define ALIGNED_BUFSIZE DMA_ALIGN_UP(ETH_BUFSIZE) + +#define RXTABLE_SIZE (STM32H7_NETHERNET * CONFIG_STM32H7_ETH_NRXDESC) +#define TXTABLE_SIZE (STM32H7_NETHERNET * CONFIG_STM32H7_ETH_NTXDESC) + +#define RXBUFFER_SIZE (CONFIG_STM32H7_ETH_NRXDESC * ALIGNED_BUFSIZE) +#define RXBUFFER_ALLOC (STM32H7_NETHERNET * RXBUFFER_SIZE) + +#define TXBUFFER_SIZE (STM32_ETH_NFREEBUFFERS * ALIGNED_BUFSIZE) +#define TXBUFFER_ALLOC (STM32H7_NETHERNET * TXBUFFER_SIZE) + +/* Extremely detailed register debug that you would normally never want + * enabled. + */ + +#ifndef CONFIG_DEBUG_NET_INFO +# undef CONFIG_STM32H7_ETHMAC_REGDEBUG +#endif + +/* Clocking *****************************************************************/ + +/* Set MACMDIOAR CR bits depending on HCLK setting */ + +#if STM32_HCLK_FREQUENCY >= 20000000 && STM32_HCLK_FREQUENCY < 35000000 +# define ETH_MACMDIOAR_CR ETH_MACMDIOAR_CR_DIV16 +#elif STM32_HCLK_FREQUENCY >= 35000000 && STM32_HCLK_FREQUENCY < 60000000 +# define ETH_MACMDIOAR_CR ETH_MACMDIOAR_CR_DIV26 +#elif STM32_HCLK_FREQUENCY >= 60000000 && STM32_HCLK_FREQUENCY < 100000000 +# define ETH_MACMDIOAR_CR ETH_MACMDIOAR_CR_DIV42 +#elif STM32_HCLK_FREQUENCY >= 100000000 && STM32_HCLK_FREQUENCY < 150000000 +# define ETH_MACMDIOAR_CR ETH_MACMDIOAR_CR_DIV62 +#elif STM32_HCLK_FREQUENCY >= 150000000 && STM32_HCLK_FREQUENCY <= 216000000 +# define ETH_MACMDIOAR_CR ETH_MACMDIOAR_CR_DIV102 +#else +# error "STM32_HCLK_FREQUENCY not supportable" +#endif + +/* Timing *******************************************************************/ + +/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per + * second + */ + +#define STM32_WDDELAY (1*CLK_TCK) + +/* TX timeout = 1 minute */ + +#define STM32_TXTIMEOUT (60*CLK_TCK) + +/* PHY reset/configuration delays in milliseconds */ + +#define PHY_RESET_DELAY (65) +#define PHY_CONFIG_DELAY (1000) + +/* PHY read/write delays in loop counts */ + +#define PHY_READ_TIMEOUT (0x0004ffff) +#define PHY_WRITE_TIMEOUT (0x0004ffff) +#define PHY_RETRY_TIMEOUT (0x0004ffff) + +/* Register values **********************************************************/ + +/* Clear the MACCR bits that will be setup during MAC initialization (or that + * are cleared unconditionally). Per the reference manual, all reserved bits + * must be retained at their reset value. + * + * ETH_MACCR_RE Bit 0: Receiver enable + * ETH_MACCR_TE Bit 1: Transmitter enable + * ETH_MACCR_PRELEN Bits 2-3: Preamble length for transmit packets + * ETH_MACCR_DC Bit 4: Deferral check + * ETH_MACCR_BL Bits 5-6: Back-off limit + * ETH_MACCR_DR Bit 8: Retry disable + * ETH_MACCR_DCRS Bit 9: Carrier sense disable + * ETH_MACCR_DO Bit 10: Receive own disable + * ETH_MACCR_LM Bit 12: Loopback mode + * ETH_MACCR_DM Bit 13: Duplex mode + * ETH_MACCR_FES Bit 14: Fast Ethernet speed + * ETH_MACCR_JE Bit 16: Jumbo packet enable + * ETH_MACCR_JD Bit 17: Jabber disable + * ETH_MACCR_WD Bit 19: Watchdog disable + * ETH_MACCR_ACS Bit 20: Automatic pad/CRC stripping + * ETH_MACCR_CST Bit 21: CRC stripping for Type frames + * ETH_MACCR_S2KP Bit 22: IEEE 802.3as Support for 2K Packets + * ETH_MACCR_GPSLCE Bit 23: Giant Packet Size Limit Control Enable + * ETH_MACCR_IPG Bits 24-26: Inter-packet gap + * ETH_MACCR_IPC Bit 27: IPv4 checksum offload + * ETH_MACCR_SARC Bits 28-30: Src address insertion or replacement + * ETH_MACCR_ARPEN Bit 31: ARP Offload Enable + */ + +#define MACCR_CLEAR_BITS \ + (ETH_MACCR_RE | ETH_MACCR_TE | ETH_MACCR_PRELEN_MASK | ETH_MACCR_DC | \ + ETH_MACCR_BL_MASK | ETH_MACCR_DR | ETH_MACCR_DCRS | ETH_MACCR_DO | \ + ETH_MACCR_LM | ETH_MACCR_DM | ETH_MACCR_FES | ETH_MACCR_JE | \ + ETH_MACCR_JD | ETH_MACCR_WD | ETH_MACCR_ACS | ETH_MACCR_CST | \ + ETH_MACCR_S2KP | ETH_MACCR_GPSLCE | ETH_MACCR_IPG_MASK | \ + ETH_MACCR_IPC | ETH_MACCR_SARC_MASK | ETH_MACCR_ARPEN) + +/* The following bits are set or left zero unconditionally in all modes. + * + * ETH_MACCR_RE Receiver enable 0 (disabled) + * ETH_MACCR_TE Transmitter enable 0 (disabled) + * ETH_MACCR_DC Deferral check 0 (disabled) + * ETH_MACCR_BL Back-off limit 0 (10) + * ETH_MACCR_ACS Automatic pad/CRC stripping 0 (disabled) + * ETH_MACCR_DR Retry disable 1 (disabled) + * ETH_MACCR_IPC IPv4 checksum offload Depends on CONFIG_STM32H7_ETH_HWCHECKSUM + * ETH_MACCR_LM Loopback mode 0 (disabled) + * ETH_MACCR_DO Receive own disable 0 (enabled) + * ETH_MACCR_DCRS Carrier sense disable 0 (enabled) + * ETH_MACCR_IPG Inter-packet gap 0 (96 bits) + * ETH_MACCR_JD Jabber disable 0 (enabled) + * ETH_MACCR_WD Watchdog disable 0 (enabled) + * ETH_MACCR_CST CRC stripping for Type frames 0 (disabled) + * + * The following are set conditionally based on mode and speed. + * + * ETH_MACCR_DM Duplex mode Depends on priv->fduplex + * ETH_MACCR_FES Fast Ethernet speed Depends on priv->mbps100 + */ + +#ifdef CONFIG_STM32H7_ETH_HWCHECKSUM +# define MACCR_SET_BITS \ + (ETH_MACCR_BL_10 | ETH_MACCR_DR | ETH_MACCR_IPC | ETH_MACCR_IPG(96)) +#else +# define MACCR_SET_BITS \ + (ETH_MACCR_BL_10 | ETH_MACCR_DR | ETH_MACCR_IPG(96)) +#endif + +/* Clear the MACCR bits that will be setup during MAC initialization (or that + * are cleared unconditionally). Per the reference manual, all reserved bits + * must be retained at their reset value. + * + * ETH_MACPFR_PM Bit 0: Promiscuous mode + * ETH_MACPFR_HUC Bit 1: Hash unicast + * ETH_MACPFR_HMC Bit 2: Hash multicast + * ETH_MACPFR_DAIF Bit 3: Destination address inverse filtering + * ETH_MACPFR_PM Bit 4: Pass all multicast + * ETH_MACPFR_DBF Bit 5: Broadcast frames disable + * ETH_MACPFR_PCF Bits 6-7: Pass control frames + * ETH_MACPFR_SAIF Bit 8: Source address inverse filtering + * ETH_MACPFR_SAF Bit 9: Source address filter + * ETH_MACPFR_HPF Bit 10: Hash or perfect filter + * ETH_MACPFR_VTFE Bit 16: VLAN Tag Filter Enable + * ETH_MACPFR_IPFE Bit 20: Layer 3 and Layer 4 Filter Enable + * ETH_MACPFR_DNTU Bit 21: Drop Non-TCP/UDP over IP Packets + * ETH_MACPFR_RA Bit 31: Receive all + */ + +#define MACPFR_CLEAR_BITS \ + (ETH_MACPFR_PM | ETH_MACPFR_HUC | ETH_MACPFR_HMC | ETH_MACPFR_DAIF | \ + ETH_MACPFR_PM | ETH_MACPFR_DBF | ETH_MACPFR_PCF_MASK | ETH_MACPFR_SAIF | \ + ETH_MACPFR_SAF | ETH_MACPFR_HPF | ETH_MACPFR_VTFE | ETH_MACPFR_IPFE | \ + ETH_MACPFR_DNTU | ETH_MACPFR_RA) + +/* The following bits are set or left zero unconditionally in all modes. + * + * ETH_MACPFR_PM Promiscuous mode 0 (disabled) + * ETH_MACPFR_HU Hash unicast 0 (perfect dest filtering) + * ETH_MACPFR_HM Hash multicast 0 (perfect dest filtering) + * ETH_MACPFR_DAIF Destination address inverse filtering 0 (normal) + * ETH_MACPFR_PAM Pass all multicast 0 (Depends on HM bit) + * ETH_MACPFR_BFD Broadcast frames disable 0 (enabled) + * ETH_MACPFR_PCF Pass control frames 1 (block all but PAUSE) + * ETH_MACPFR_SAIF Source address inverse filtering 0 (not used) + * ETH_MACPFR_SAF Source address filter 0 (disabled) + * ETH_MACPFR_HPF Hash or perfect filter 0 (Only matching frames passed) + * ETH_MACPFR_RA Receive all 1 (enabled) + */ + +/* TODO: use proper mac filtering and not RA */ + +#define MACPFR_SET_BITS (ETH_MACPFR_RA | ETH_MACPFR_PCF_PAUSE) + +/* Clear the MACQTXFCR and MACRXFCR bits that will be setup during MAC + * initialization (or that are cleared unconditionally). Per the reference + * manual, all reserved bits must be retained at their reset value. + * + * ETH_MACQTXFCR_FCB_BPA Bit 0: Flow control busy/back pressure activate + * ETH_MACQTXFCR_TFE Bit 1: Transmit flow control enable + + * ETH_MACQTXFCR_PLT Bits 4-6: Pause low threshold + * ETH_MACQTXFCR_DZPQ Bit 7: Zero-quanta pause disable + * ETH_MACQTXFCR_PT Bits 16-31: Pause time + + * ETH_MACRXFCR_RFE Bit 0: Receive flow control enable + * ETH_MACRXFCR_UP Bit 1: Unicast pause frame detect + */ + +#define MACQTXFCR_CLEAR_MASK \ + (ETH_MACQTXFCR_FCB_BPA | ETH_MACQTXFCR_TFE | \ + ETH_MACQTXFCR_PLT_MASK | ETH_MACQTXFCR_DZPQ | ETH_MACQTXFCR_PT_MASK) + +#define MACRXFCR_CLEAR_MASK \ + (ETH_MACRXFCR_RFE | ETH_MACRXFCR_UP) + +/* The following bits are set or left zero unconditionally in all modes. + * + * ETH_MACQTXFCR_FCB_BPA Flow control busy/back pressure activate 0 (no pause control frame) + * ETH_MACQTXFCR_TFE Transmit flow control enable 0 (disabled) + * ETH_MACQTXFCR_PLT Pause low threshold 0 (pause time - 4) + * ETH_MACQTXFCR_DZPQ Zero-quanta pause disable 1 (disabled) + * ETH_MACQTXFCR_PT Pause time 0 + * ETH_MACRXFCR_RFE Receive flow control enable 0 (disabled) + * ETH_MACRXFCR_UP Unicast pause frame detect 0 (disabled) + */ + +#define MACQTXFCR_SET_MASK (ETH_MACQTXFCR_PLT_M4 | ETH_MACQTXFCR_DZPQ) +#define MACRXFCR_SET_MASK (0) + +/* Clear the MTLTXQOMR bits that will be setup during MAC initialization (or that + * are cleared unconditionally). Per the reference manual, all reserved bits + * must be retained at their reset value. + * ETH_MTLTXQOMR_FTQ Bit 0: Flush Transmit Queue + * ETH_MTLTXQOMR_TSF Bit 1: Transmit Store and Forward + * ETH_MTLTXQOMR_TXQEN Bits 2-3: Transmit Queue Enable + * ETH_MTLTXQOMR_TTC Bits 4-6: Transmit Threshold Control + * ETH_MTLTXQOMR_TQS Bits 16-24: Transmit Queue Size + */ + +#define MTLTXQOMR_CLEAR_MASK \ + (ETH_MTLTXQOMR_FTQ | ETH_MTLTXQOMR_TSF | \ + ETH_MTLTXQOMR_TXQEN_MASK | ETH_MTLTXQOMR_TTC_MASK | \ + ETH_MTLTXQOMR_TQS_MASK) + +/* Clear the MTLRXQOMR bits that will be setup during MAC initialization (or that + * are cleared unconditionally). Per the reference manual, all reserved bits + * must be retained at their reset value. + * + * ETH_MTLRXQOMR_RTC_MASK Bits 0-1: Receive Queue Threshold Control + * ETH_MTLRXQOMR_FUP Bit 3: Forward Undersized Good Packets + * ETH_MTLRXQOMR_FEP Bit 4: Forward Error Packets + * ETH_MTLRXQOMR_RSF Bit 5: Receive Queue Store and Forward + * ETH_MTLRXQOMR_DIS_TCP_EF Bit 6: Disable Dropping of TCP/IP Checksum Error Packets + * ETH_MTLRXQOMR_EHFC Bit 7: Enable Hardware Flow Control + * ETH_MTLRXQOMR_RFA_MASK Bits 8-10: Threshold for Activating Flow Control + * ETH_MTLRXQOMR_RFD_MASK Bits 14-16: Threshold for Deactivating Flow Control + * ETH_MTLRXQOMR_RQS_MASK Bits 20-22: Receive Queue Size + */ + +#define MTLRXQOMR_CLEAR_MASK \ + (ETH_MTLRXQOMR_RTC_MASK | ETH_MTLRXQOMR_FUP | \ + ETH_MTLRXQOMR_FEP | ETH_MTLRXQOMR_RSF | \ + ETH_MTLRXQOMR_DIS_TCP_EF | ETH_MTLRXQOMR_EHFC | \ + ETH_MTLRXQOMR_RFA_MASK | ETH_MTLRXQOMR_RFD_MASK | \ + ETH_MTLRXQOMR_RQS_MASK) + +#define MTLTXQOMR_SET_MASK \ + ((0xd << ETH_MTLTXQOMR_TQS_SHIFT) | ETH_MTLTXQOMR_TTC_64 | \ + ETH_MTLTXQOMR_TXQEN_ENABLED | ETH_MTLTXQOMR_FTQ) + +/* TODO: Check whether use flow control (bits RFD, RFA EHFC) */ + +#define MTLRXQOMR_SET_MASK \ + ((0x7 << ETH_MTLRXQOMR_RQS_SHIFT) | ETH_MTLRXQOMR_RTC_64) + +#ifdef CONFIG_STM32H7_ETH_HWCHECKSUM +/* TODO */ + +# error CONFIG_STM32H7_ETH_HWCHECKSUM not supported +#endif + +/* Clear the DMAMR bits that will be setup during MAC initialization (or that + * are cleared unconditionally). Per the reference manual, all reserved bits + * must be retained at their reset value. + * + * ETH_DMAMR_SWR Bit 0: Software Reset + * ETH_DMAMR_DA Bit 1: DMA Tx or Rx Arbitration Scheme + * ETH_DMAMR_TXPR Bit 11: Transmit priority + * ETH_DMAMR_PR Bits 12-14: Priority ratio + * ETH_DMAMR_INTM Bits 16-17: Interrupt Mode + */ + +#define DMAMR_CLEAR_MASK \ + (ETH_DMAMR_SWR | ETH_DMAMR_DA | ETH_DMAMR_TXPR | ETH_DMAMR_PR_MASK | \ + ETH_DMAMR_INTM_MASK) + +#define DMAMR_SET_MASK (ETH_DMAMR_PR_2TO1) + +/* TODO: Comment these bits */ + +#define DMACCR_CLEAR_MASK \ + ( ETH_DMACCR_MSS_MASK | ETH_DMACCR_PBLX8 | ETH_DMACCR_DSL_MASK) + +/* Set the descriptor skip length in 32-bit words */ + +#define DMACCR_SET_MASK \ + (ETH_DMACCR_DSL((DESC_PADSIZE - DESC_SIZE)/4)) + +#define DMASBMR_CLEAR_MASK \ + (STM32_ETH_DMASBMR_FB | STM32_ETH_DMASBMR_AAL | \ + STM32_ETH_DMASBMR_MB | STM32_ETH_DMASBMR_RB) + +#define DMASBMR_SET_MASK \ + (STM32_ETH_DMASBMR_FB | STM32_ETH_DMASBMR_AAL) + +#define DMACTXCR_CLEAR_MASK \ + (ETH_DMACTXCR_ST | ETH_DMACTXCR_ST | ETH_DMACTXCR_OSF | \ + ETH_DMACTXCR_TSE | ETH_DMACTXCR_TXPBL_MASK) + +#define DMACTXCR_SET_MASK \ + (ETH_DMACTXCR_OSF | ETH_DMACTXCR_TXPBL(32)) + +#define DMACRXCR_CLEAR_MASK \ + (ETH_DMACRXCR_SR | ETH_DMACRXCR_RBSZ_MASK | \ + ETH_DMACRXCR_RXPBL_MASK | ETH_DMACRXCR_RPF) + +#define DMACRXCR_SET_MASK \ + (ETH_DMACRXCR_RBSZ(ALIGNED_BUFSIZE) | ETH_DMACRXCR_RXPBL(32)) + +/* Interrupt bit sets *******************************************************/ + +/* All interrupts in the normal and abnormal interrupt summary. Early + * transmit interrupt (ETI) is excluded from the abnormal set because it + * causes too many interrupts and is not interesting. + */ + +#define ETH_DMAINT_NORMAL \ + (ETH_DMACIER_TIE | ETH_DMACIER_TBUE | ETH_DMACIER_RIE | ETH_DMACIER_ERIE) + +#define ETH_DMAINT_ABNORMAL \ + (ETH_DMACIER_TXSE \ + ETH_DMACIER_RBUE | ETH_DMACIER_RSE | ETH_DMACIER_RWTE | /* ETH_DMACIER_ETIE | */ \ + ETH_DMACIER_FBEE) + +/* Normal receive, transmit, error interrupt enable bit sets */ + +#define ETH_DMAINT_RECV_ENABLE (ETH_DMACIER_NIE | ETH_DMACIER_RIE) +#define ETH_DMAINT_XMIT_ENABLE (ETH_DMACIER_NIE | ETH_DMACIER_TIE) +#define ETH_DMAINT_XMIT_DISABLE (ETH_DMACIER_TIE) + +#ifdef CONFIG_DEBUG_NET +# define ETH_DMAINT_ERROR_ENABLE (ETH_DMACIER_AIE | ETH_DMACIER_ABNORMAL) +#else +# define ETH_DMAINT_ERROR_ENABLE (0) +#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 + ****************************************************************************/ + +/* This union type forces the allocated size of TX&RX descriptors to be the + * padded to a exact multiple of the Cortex-M7 D-Cache line size. + */ + +union stm32_desc_u +{ + uint8_t pad[DESC_PADSIZE]; + struct eth_desc_s desc; +}; + +/* The stm32_ethmac_s encapsulates all state information for a single hardware + * interface + */ + +struct stm32_ethmac_s +{ + uint8_t ifup : 1; /* true:ifup false:ifdown */ + uint8_t mbps100 : 1; /* 100MBps operation (vs 10 MBps) */ + uint8_t fduplex : 1; /* Full (vs. half) duplex */ + uint8_t intf; /* Ethernet interface number */ + WDOG_ID txpoll; /* TX poll timer */ + WDOG_ID 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 */ + + /* Used to track transmit and receive descriptors */ + + struct eth_desc_s *txhead; /* Next available TX descriptor */ + struct eth_desc_s *rxhead; /* Next available RX descriptor */ + + struct eth_desc_s *txchbase; /* TX descriptor ring base address */ + struct eth_desc_s *rxchbase; /* RX descriptor ring base address */ + + struct eth_desc_s *txtail; /* First "in_flight" TX descriptor */ + struct eth_desc_s *rxcurr; /* First RX descriptor of the segment */ + uint16_t segments; /* RX segment count */ + uint16_t inflight; /* Number of TX transfers "in_flight" */ + sq_queue_t freeb; /* The free buffer list */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* DMA buffers. DMA buffers must: + * + * 1. Be a multiple of the D-Cache line size. This requirement is assured + * by the definition of RXDMA buffer size above. + * 2. Be aligned a D-Cache line boundaries, and + * 3. Be positioned in DMA-able memory. This must be managed by logic + * in the linker script file. + * + * These DMA buffers are defined sequentially here to best assure optimal + * packing of the buffers. + */ + +/* Descriptor allocations */ + +static union stm32_desc_u g_rxtable[RXTABLE_SIZE] +__attribute__((aligned(ARMV7M_DCACHE_LINESIZE))); +static union stm32_desc_u g_txtable[TXTABLE_SIZE] +__attribute__((aligned(ARMV7M_DCACHE_LINESIZE))); + +/* Buffer allocations */ + +static uint8_t g_rxbuffer[RXBUFFER_ALLOC] +__attribute__((aligned(ARMV7M_DCACHE_LINESIZE))); +static uint8_t g_txbuffer[TXBUFFER_ALLOC] +__attribute__((aligned(ARMV7M_DCACHE_LINESIZE))); + +/* These are the pre-allocated Ethernet device structures */ + +static struct stm32_ethmac_s g_stm32ethmac[STM32H7_NETHERNET]; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register operations ******************************************************/ + +#ifdef CONFIG_STM32H7_ETHMAC_REGDEBUG +static uint32_t stm32_getreg(uint32_t addr); +static void stm32_putreg(uint32_t val, uint32_t addr); +static void stm32_checksetup(void); +#else +# define stm32_getreg(addr) getreg32(addr) +# define stm32_putreg(val,addr) putreg32(val,addr) +# define stm32_checksetup() +#endif + +/* Free buffer management */ + +static void stm32_initbuffer(struct stm32_ethmac_s *priv, + uint8_t *txbuffer); +static inline uint8_t *stm32_allocbuffer(struct stm32_ethmac_s *priv); +static inline void stm32_freebuffer(struct stm32_ethmac_s *priv, + uint8_t *buffer); +static inline bool stm32_isfreebuffer(struct stm32_ethmac_s *priv); + +/* Common TX logic */ + +static int stm32_transmit(struct stm32_ethmac_s *priv); +static int stm32_txpoll(struct net_driver_s *dev); +static void stm32_dopoll(struct stm32_ethmac_s *priv); + +/* Interrupt handling */ + +static void stm32_enableint(struct stm32_ethmac_s *priv, uint32_t ierbit); +static void stm32_disableint(struct stm32_ethmac_s *priv, uint32_t ierbit); + +static void stm32_freesegment(struct stm32_ethmac_s *priv, + struct eth_desc_s *rxfirst, int segments); +static int stm32_recvframe(struct stm32_ethmac_s *priv); +static void stm32_receive(struct stm32_ethmac_s *priv); +static void stm32_freeframe(struct stm32_ethmac_s *priv); +static void stm32_txdone(struct stm32_ethmac_s *priv); + +static void stm32_interrupt_work(void *arg); +static int stm32_interrupt(int irq, void *context, FAR void *arg); + +/* Watchdog timer expirations */ + +static void stm32_txtimeout_work(void *arg); +static void stm32_txtimeout_expiry(int argc, uint32_t arg, ...); + +static void stm32_poll_work(void *arg); +static void stm32_poll_expiry(int argc, uint32_t arg, ...); + +/* NuttX callback functions */ + +static int stm32_ifup(struct net_driver_s *dev); +static int stm32_ifdown(struct net_driver_s *dev); + +static void stm32_txavail_work(void *arg); +static int stm32_txavail(struct net_driver_s *dev); + +#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) +static int stm32_addmac(struct net_driver_s *dev, const uint8_t *mac); +#endif +#ifdef CONFIG_NET_IGMP +static int stm32_rmmac(struct net_driver_s *dev, const uint8_t *mac); +#endif +#ifdef CONFIG_NETDEV_PHY_IOCTL +static int stm32_ioctl(struct net_driver_s *dev, int cmd, + unsigned long arg); +#endif + +/* Descriptor Initialization */ + +static void stm32_txdescinit(struct stm32_ethmac_s *priv, + union stm32_desc_u *txtable); +static void stm32_rxdescinit(struct stm32_ethmac_s *priv, + union stm32_desc_u *rxtable, uint8_t *rxbuffer); + +/* PHY Initialization */ + +#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT) +static int stm32_phyintenable(struct stm32_ethmac_s *priv); +#endif +static int stm32_phyread(uint16_t phydevaddr, uint16_t phyregaddr, + uint16_t *value); +static int stm32_phywrite(uint16_t phydevaddr, uint16_t phyregaddr, + uint16_t value, uint16_t mask); +#ifdef CONFIG_ETH0_PHY_DM9161 +static inline int stm32_dm9161(struct stm32_ethmac_s *priv); +#endif +static int stm32_phyinit(struct stm32_ethmac_s *priv); +#ifdef CONFIG_STM32H7_ETHMAC_REGDEBUG +static void stm32_phyregdump(void); +#endif + +/* MAC/DMA Initialization */ + +#ifdef CONFIG_STM32H7_MII +static inline void stm32_selectmii(void); +#endif +#ifdef CONFIG_STM32H7_RMII +static inline void stm32_selectrmii(void); +#endif +static inline void stm32_ethgpioconfig(struct stm32_ethmac_s *priv); +static void stm32_ethreset(struct stm32_ethmac_s *priv); +static int stm32_macconfig(struct stm32_ethmac_s *priv); +static void stm32_macaddress(struct stm32_ethmac_s *priv); +#ifdef CONFIG_NET_ICMPv6 +static void stm32_ipv6multicast(struct stm32_ethmac_s *priv); +#endif +static int stm32_macenable(struct stm32_ethmac_s *priv); +static int stm32_ethconfig(struct stm32_ethmac_s *priv); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ +/**************************************************************************** + * Name: stm32_getreg + * + * Description: + * This function may to used to intercept an monitor all register accesses. + * Clearly this is nothing you would want to do unless you are debugging + * this driver. + * + * Input Parameters: + * addr - The register address to read + * + * Returned Value: + * The value read from the register + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_ETHMAC_REGDEBUG +static uint32_t stm32_getreg(uint32_t addr) +{ + static uint32_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + + /* Read the value from the register */ + + uint32_t val = getreg32(addr); + + /* Is this the same value that we read from the same register last time? + * Are we polling the register? If so, suppress some of the output. + */ + + if (addr == prevaddr && val == preval) + { + if (count == 0xffffffff || ++count > 3) + { + if (count == 4) + { + ninfo("...\n"); + } + + return val; + } + } + + /* No this is a new address or value */ + + else + { + /* Did we print "..." for the previous value? */ + + if (count > 3) + { + /* Yes.. then show how many times the value repeated */ + + ninfo("[repeats %d more times]\n", count - 3); + } + + /* Save the new address, value, and count */ + + prevaddr = addr; + preval = val; + count = 1; + } + + /* Show the register value read */ + + ninfo("%08x->%08x\n", addr, val); + return val; +} +#endif + +/**************************************************************************** + * Name: stm32_putreg + * + * Description: + * This function may to used to intercept an monitor all register accesses. + * Clearly this is nothing you would want to do unless you are debugging + * this driver. + * + * Input Parameters: + * val - The value to write to the register + * addr - The register address to read + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_ETHMAC_REGDEBUG +static void stm32_putreg(uint32_t val, uint32_t addr) +{ + /* Show the register value being written */ + + ninfo("%08x<-%08x\n", addr, val); + + /* Write the value */ + + putreg32(val, addr); +} +#endif + +/**************************************************************************** + * Name: stm32_checksetup + * + * Description: + * Show the state of critical configuration registers. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_ETHMAC_REGDEBUG +static void stm32_checksetup(void) +{ +} +#endif + +/**************************************************************************** + * Function: stm32_initbuffer + * + * Description: + * Initialize the free buffer list. + * + * Parameters: + * priv - Reference to the driver state structure + * txbuffer - DMA memory allocated for TX buffers. + * + * Returned Value: + * None + * + * Assumptions: + * Called during early driver initialization before Ethernet interrupts + * are enabled. + * + ****************************************************************************/ + +static void stm32_initbuffer(struct stm32_ethmac_s *priv, uint8_t *txbuffer) +{ + uint8_t *buffer; + int i; + + /* Initialize the head of the free buffer list */ + + sq_init(&priv->freeb); + + /* Add all of the pre-allocated buffers to the free buffer list */ + + for (i = 0, buffer = txbuffer; + i < STM32_ETH_NFREEBUFFERS; + i++, buffer += ALIGNED_BUFSIZE) + { + sq_addlast((sq_entry_t *)buffer, &priv->freeb); + } +} + +/**************************************************************************** + * Function: stm32_allocbuffer + * + * Description: + * Allocate one buffer from the free buffer list. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * Pointer to the allocated buffer on success; NULL 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 inline uint8_t *stm32_allocbuffer(struct stm32_ethmac_s *priv) +{ + /* Allocate a buffer by returning the head of the free buffer list */ + + return (uint8_t *)sq_remfirst(&priv->freeb); +} + +/**************************************************************************** + * Function: stm32_freebuffer + * + * Description: + * Return a buffer to the free buffer list. + * + * Parameters: + * priv - Reference to the driver state structure + * buffer - A pointer to the buffer to be freed + * + * Returned Value: + * None + * + * 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 inline void stm32_freebuffer(struct stm32_ethmac_s *priv, + uint8_t *buffer) +{ + /* Free the buffer by adding it to to the end of the free buffer list */ + + sq_addlast((sq_entry_t *)buffer, &priv->freeb); +} + +/**************************************************************************** + * Function: stm32_isfreebuffer + * + * Description: + * Return TRUE if the free buffer list is not empty. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * True if there are one or more buffers in the free buffer list; + * false if the free buffer list is empty + * + * Assumptions: + * None. + * + ****************************************************************************/ + +static inline bool stm32_isfreebuffer(struct stm32_ethmac_s *priv) +{ + /* Return TRUE if the free buffer list is not empty */ + + return !sq_empty(&priv->freeb); +} + +/**************************************************************************** + * Function: stm32_get_next_txdesc + * + * Description: + * Returns the next tx descriptor in the list + * + * Parameters: + * priv - Reference to the driver state structure + * curr - Pointer to a tx descriptor + * + * Returned Value: + * pointer to the next tx descriptor for the current interface + * + ****************************************************************************/ +static struct eth_desc_s *stm32_get_next_txdesc(struct stm32_ethmac_s *priv, + struct eth_desc_s * curr) +{ + union stm32_desc_u *first = + &g_txtable[priv->intf * CONFIG_STM32H7_ETH_NTXDESC]; + union stm32_desc_u *last = + &g_txtable[priv->intf * CONFIG_STM32H7_ETH_NTXDESC + + CONFIG_STM32H7_ETH_NTXDESC - 1]; + union stm32_desc_u *next = ((union stm32_desc_u *)curr) + 1; + + if (next > last) + { + next = first; + } + + return &next->desc; +} + +/**************************************************************************** + * Function: stm32_transmit + * + * Description: + * Start hardware transmission. Called either from the txdone interrupt + * handling or from watchdog based polling. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + ****************************************************************************/ + +static int stm32_transmit(struct stm32_ethmac_s *priv) +{ + struct eth_desc_s *txdesc; + struct eth_desc_s *txfirst; + + /* The internal (optimal) network buffer size may be configured to be larger + * than the Ethernet buffer size. + */ + +#if OPTIMAL_ETH_BUFSIZE > ALIGNED_BUFSIZE + uint8_t *buffer; + int bufcount; + int lastsize; + int i; +#endif + + /* Verify that the hardware is ready to send another packet. If we get + * here, then we are committed to sending a packet; Higher level logic + * must have assured that there is no transmission in progress. + */ + + txdesc = priv->txhead; + txfirst = txdesc; + + ninfo("d_len: %d d_buf: %p txhead: %p tdes3: %08x\n", + priv->dev.d_len, priv->dev.d_buf, txdesc, txdesc->des3); + + DEBUGASSERT(txdesc && (txdesc->des3 & ETH_TDES3_RD_OWN) == 0); + + /* Flush the contents of the TX buffer into physical memory */ + + up_clean_dcache((uintptr_t)priv->dev.d_buf, + (uintptr_t)priv->dev.d_buf + priv->dev.d_len); + + /* Is the size to be sent greater than the size of the Ethernet buffer? */ + + DEBUGASSERT(priv->dev.d_len > 0 && priv->dev.d_buf != NULL); + +#if OPTIMAL_ETH_BUFSIZE > ALIGNED_BUFSIZE + if (priv->dev.d_len > ALIGNED_BUFSIZE) + { + /* Yes... how many buffers will be need to send the packet? */ + + bufcount = (priv->dev.d_len + (ALIGNED_BUFSIZE - 1)) / ALIGNED_BUFSIZE; + lastsize = priv->dev.d_len - (bufcount - 1) * ALIGNED_BUFSIZE; + + ninfo("bufcount: %d lastsize: %d\n", bufcount, lastsize); + + /* Set the first segment bit in the first TX descriptor */ + + txdesc->des3 = ETH_TDES3_RD_FD; + + /* Set up all but the last TX descriptor */ + + buffer = priv->dev.d_buf; + + for (i = 0; i < bufcount; i++) + { + /* This could be a normal event but the design does not handle it */ + + DEBUGASSERT((txdesc->des3 & ETH_TDES3_RD_OWN) == 0); + + /* Set the Buffer1 address pointer */ + + txdesc->des0 = (uint32_t)buffer; + + /* Set the Buffer2 address pointer */ + + txdesc->des1 = 0; + + /* Set the buffer size in all TX descriptors */ + + if (i == (bufcount - 1)) + { + /* This is the last segment. Set the last segment bit in the + * last TX descriptor + */ + + txdesc->des3 |= ETH_TDES3_RD_LD; + + /* This segement is, most likely, of fractional buffersize */ + + /* ask for an interrupt when this segment transfer completes. */ + + txdesc->des2 = lastsize | ETH_TDES2_RD_IOC; + buffer += lastsize; + } + else + { + /* This is not the last segment. We don't want an interrupt + * when this segment transfer completes. + */ + + /* The size of the transfer is the whole buffer */ + + txdesc->des2 = ALIGNED_BUFSIZE | ETH_TDES2_RD_IOC; + buffer += ALIGNED_BUFSIZE; + } + + /* Give the descriptor to DMA */ + + txdesc->des3 |= ETH_TDES3_RD_OWN; + + /* Flush the contents of the modified TX descriptor into physical + * memory. + */ + + up_clean_dcache((uintptr_t)txdesc, + (uintptr_t)txdesc + sizeof(struct eth_desc_s)); + + /* Point to the next available TX descriptor */ + + txdesc = stm32_get_next_txdesc(priv, txdesc); + } + } + else +#endif + { + /* Set the Buffer1 address pointer */ + + txdesc->des0 = (uint32_t)priv->dev.d_buf; + + /* Set the Buffer2 address pointer */ + + txdesc->des1 = 0; + + /* Set frame size, and we do + * want an interrupt when the transfer completes. + */ + + DEBUGASSERT(priv->dev.d_len <= CONFIG_NET_ETH_PKTSIZE); + txdesc->des2 = priv->dev.d_len | ETH_TDES2_RD_IOC; + + /* The single descriptor is both the first and last segment. */ + + /* Set OWN bit of the TX descriptor des3. This gives the buffer to + * Ethernet DMA + */ + + txdesc->des3 = (ETH_TDES3_RD_OWN | ETH_TDES3_RD_LD | ETH_TDES3_RD_FD); + + /* Flush the contents of the modified TX descriptor into physical + * memory. + */ + + up_clean_dcache((uintptr_t)txdesc, + (uintptr_t)txdesc + sizeof(struct eth_desc_s)); + + /* Point to the next available TX descriptor */ + + txdesc = stm32_get_next_txdesc(priv, txdesc); + } + + /* Remember where we left off in the TX descriptor chain */ + + priv->txhead = txdesc; + + /* Detach the buffer from priv->dev structure. That buffer is now + * "in-flight". + */ + + priv->dev.d_buf = NULL; + priv->dev.d_len = 0; + + /* If there is no other TX buffer, in flight, then remember the location + * of the TX descriptor. This is the location to check for TX done events. + */ + + if (!priv->txtail) + { + DEBUGASSERT(priv->inflight == 0); + priv->txtail = txfirst; + } + + /* Increment the number of TX transfer in-flight */ + + priv->inflight++; + + ninfo("txhead: %p txtail: %p inflight: %d\n", + priv->txhead, priv->txtail, priv->inflight); + + /* If all TX descriptors are in-flight, then we have to disable receive + * interrupts too. This is because receive events can trigger more un- + * stoppable transmit events. + */ + + if (priv->inflight >= CONFIG_STM32H7_ETH_NTXDESC) + { + stm32_disableint(priv, ETH_DMACIER_RIE); + } + + /* Check if the TX Buffer unavailable flag is set */ + + MEMORY_SYNC(); + + /* Enable TX interrupts */ + + stm32_enableint(priv, ETH_DMACIER_TIE); + + /* Setup the TX timeout watchdog (perhaps restarting the timer) */ + + (void)wd_start(priv->txtimeout, STM32_TXTIMEOUT, stm32_txtimeout_expiry, + 1, (uint32_t)priv); + + /* Update the tx descriptor tail pointer register to start the DMA */ + + putreg32((uintptr_t)txdesc, STM32_ETH_DMACTXDTPR); + + return OK; +} + +/**************************************************************************** + * Function: stm32_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 + * + * 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 stm32_txpoll(struct net_driver_s *dev) +{ + struct stm32_ethmac_s *priv = (struct stm32_ethmac_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 */ + + /* Send the packet */ + + stm32_transmit(priv); + DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL); + + /* Check if the next TX descriptor is owned by the Ethernet DMA or + * CPU. We cannot perform the TX poll if we are unable to accept + * another packet for transmission. + * + * In a race condition, ETH_TDES3_OWN may be cleared BUT still not + * available because stm32_freeframe() has not yet run. If + * stm32_freeframe() has run, the buffer1 pointer (tdes2) will be + * nullified (and inflight should be < CONFIG_STM32H7_ETH_NTXDESC). + */ + + if ((priv->txhead->des3 & ETH_TDES3_RD_OWN) != 0 || + priv->txhead->des0 != 0) + { + /* We have to terminate the poll if we have no more descriptors + * available for another transfer. + */ + + return -EBUSY; + } + + /* We have the descriptor, we can continue the poll. Allocate a new + * buffer for the poll. + */ + + dev->d_buf = stm32_allocbuffer(priv); + + /* We can't continue the poll if we have no buffers */ + + if (dev->d_buf == NULL) + { + /* Terminate the poll. */ + + return -ENOMEM; + } + } + + /* If zero is returned, the polling will continue until all connections have + * been examined. + */ + + return 0; +} + +/**************************************************************************** + * Function: stm32_dopoll + * + * Description: + * The function is called in order to perform an out-of-sequence TX poll. + * This is done: + * + * 1. After completion of a transmission (stm32_txdone), + * 2. When new TX data is available (stm32_txavail_process), and + * 3. After a TX timeout to restart the sending process + * (stm32_txtimeout_process). + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void stm32_dopoll(struct stm32_ethmac_s *priv) +{ + struct net_driver_s *dev = &priv->dev; + + /* Check if the next TX descriptor is owned by the Ethernet DMA or + * CPU. We cannot perform the TX poll if we are unable to accept + * another packet for transmission. + * + * In a race condition, ETH_TDES3_RD_OWN may be cleared BUT still not + * available because stm32_freeframe() has not yet run. If + * stm32_freeframe() has run, the buffer1 pointer (des0) will be + * nullified (and inflight should be < CONFIG_STM32H7_ETH_NTXDESC). + */ + + if ((priv->txhead->des3 & ETH_TDES3_RD_OWN) == 0 && + priv->txhead->des0 == 0) + { + /* If we have the descriptor, then poll the network for new XMIT data. + * Allocate a buffer for the poll. + */ + + DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL); + dev->d_buf = stm32_allocbuffer(priv); + + /* We can't poll if we have no buffers */ + + if (dev->d_buf) + { + (void)devif_poll(dev, stm32_txpoll); + + /* We will, most likely end up with a buffer to be freed. But it + * might not be the same one that we allocated above. + */ + + if (dev->d_buf) + { + DEBUGASSERT(dev->d_len == 0); + stm32_freebuffer(priv, dev->d_buf); + dev->d_buf = NULL; + } + } + } +} + +/**************************************************************************** + * Function: stm32_enableint + * + * Description: + * Enable a "normal" interrupt + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void stm32_enableint(struct stm32_ethmac_s *priv, uint32_t ierbit) +{ + uint32_t regval; + + /* Enable the specified "normal" interrupt */ + + regval = stm32_getreg(STM32_ETH_DMACIER); + regval |= (ETH_DMACIER_NIE | ierbit); + stm32_putreg(regval, STM32_ETH_DMACIER); +} + +/**************************************************************************** + * Function: stm32_disableint + * + * Description: + * Disable a normal interrupt. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void stm32_disableint(struct stm32_ethmac_s *priv, uint32_t ierbit) +{ + uint32_t regval; + + /* Disable the "normal" interrupt */ + + regval = stm32_getreg(STM32_ETH_DMACIER); + regval &= ~ierbit; + + /* Are all "normal" interrupts now disabled? */ + + if ((regval & ETH_DMAINT_NORMAL) == 0) + { + /* Yes.. disable normal interrupts */ + + regval &= ~ETH_DMACIER_NIE; + } + + stm32_putreg(regval, STM32_ETH_DMACIER); +} + +/**************************************************************************** + * Function: stm32_get_next_rxdesc + * + * Description: + * Returns the next rx descriptor in the list + * + * Parameters: + * priv - Reference to the driver state structure + * curr - Pointer to a rx descriptor + * + * Returned Value: + * pointer to the next rx descriptor for the current interface + * + ****************************************************************************/ +static struct eth_desc_s *stm32_get_next_rxdesc(struct stm32_ethmac_s *priv, + struct eth_desc_s * curr) +{ + union stm32_desc_u *first = + &g_rxtable[priv->intf * CONFIG_STM32H7_ETH_NRXDESC]; + union stm32_desc_u *last = + &g_rxtable[priv->intf * CONFIG_STM32H7_ETH_NRXDESC + + CONFIG_STM32H7_ETH_NRXDESC - 1]; + union stm32_desc_u *next = ((union stm32_desc_u *)curr) + 1; + + if (next > last) + { + next = first; + } + + return &next->desc; +} + +/**************************************************************************** + * Function: stm32_freesegment + * + * Description: + * The function is called when a frame is received using the DMA receive + * interrupt. It scans the RX descriptors to the received frame. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void stm32_freesegment(struct stm32_ethmac_s *priv, + struct eth_desc_s *rxfirst, int segments) +{ + struct eth_desc_s *rxdesc; + int i; + + ninfo("rxfirst: %p segments: %d\n", rxfirst, segments); + + /* Give the freed RX buffers back to the Ethernet MAC to be refilled */ + + rxdesc = rxfirst; + for (i = 0; i < segments; i++) + { + /* Set OWN bit in RX descriptors. This gives the buffers back to DMA */ + + rxdesc->des3 = ETH_RDES3_RD_OWN | ETH_RDES3_RD_IOC | ETH_RDES3_RD_BUF1V; + + /* Make sure that the modified RX descriptor is written to physical + * memory. + */ + + up_clean_dcache((uintptr_t)rxdesc, + (uintptr_t)rxdesc + sizeof(struct eth_desc_s)); + + /* Update the tail pointer */ + + stm32_putreg((uintptr_t)rxdesc, STM32_ETH_DMACRXDTPR); + + /* Get the next RX descriptor in the chain (cache coherency should not + * be an issue because the link address is constant. + */ + + rxdesc = stm32_get_next_rxdesc(priv, rxdesc); + } + + /* Reset the segment management logic */ + + priv->rxcurr = NULL; + priv->segments = 0; + + /* Check if the RX Buffer unavailable flag is set */ + + if ((stm32_getreg(STM32_ETH_DMACSR) & ETH_DMACSR_RBU) != 0) + { + /* TODO: This is probably not needed at all? */ + + nerr("ETH_DMACSR_RBU\n"); + + /* To resume processing Rx descriptors, the application should change + * the ownership of the descriptor and issue a Receive Poll Demand + * command. If this command is not issued, the Rx process resumes when + * the next recognized incoming packet is received. In ring mode, the + * application should advance the Receive Descriptor Tail Pointer + * register of a channel. This bit is set only when the DMA owns the + * previous Rx descriptor. + */ + } +} + +/**************************************************************************** + * Function: stm32_recvframe + * + * Description: + * The function is called when a frame is received using the DMA receive + * interrupt. It scans the RX descriptors of the received frame. + * + * NOTE: This function will silently discard any packets containing errors. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * OK if a packet was successfully returned; -EAGAIN if there are no + * further packets available + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static int stm32_recvframe(struct stm32_ethmac_s *priv) +{ + struct eth_desc_s *rxdesc; + struct eth_desc_s *rxcurr; + uint8_t *buffer; + int i; + + ninfo("rxhead: %p rxcurr: %p segments: %d\n", + priv->rxhead, priv->rxcurr, priv->segments); + + /* Check if there are free buffers. We cannot receive new frames in this + * design unless there is at least one free buffer. + */ + + if (!stm32_isfreebuffer(priv)) + { + nerr("ERROR: No free buffers\n"); + return -ENOMEM; + } + + /* Scan descriptors owned by the CPU. Scan until: + * + * 1) We find a descriptor still owned by the DMA, + * 2) We have examined all of the RX descriptors, or + * 3) All of the TX descriptors are in flight. + * + * This last case is obscure. It is due to that fact that each packet + * that we receive can generate an unstoppable transmisson. So we have + * to stop receiving when we can not longer transmit. In this case, the + * transmit logic should also have disabled further RX interrupts. + */ + + rxdesc = priv->rxhead; + + /* Forces the first RX descriptor to be re-read from physical memory */ + + up_invalidate_dcache((uintptr_t)rxdesc, + (uintptr_t)rxdesc + sizeof(struct eth_desc_s)); + + for (i = 0; + (rxdesc->des3 & ETH_RDES3_WB_OWN) == 0 && + i < CONFIG_STM32H7_ETH_NRXDESC && + priv->inflight < CONFIG_STM32H7_ETH_NTXDESC; + i++) + { + /* Check if this is a normal descriptor */ + + if (!(rxdesc->des3 & ETH_RDES3_WB_CTXT)) + { + /* Check if this is the first segment in the frame */ + + if ((rxdesc->des3 & ETH_RDES3_WB_FD) != 0 && + (rxdesc->des3 & ETH_RDES3_WB_LD) == 0) + { + priv->rxcurr = rxdesc; + priv->segments = 1; + } + + /* Check if this is an intermediate segment in the frame */ + + else if (((rxdesc->des3 & ETH_RDES3_WB_LD) == 0) && + ((rxdesc->des3 & ETH_RDES3_WB_FD) == 0)) + { + priv->segments++; + } + + /* Otherwise, it is the last segment in the frame */ + + else + { + priv->segments++; + + /* Check if there is only one segment in the frame */ + + if (priv->segments == 1) + { + rxcurr = rxdesc; + } + else + { + rxcurr = priv->rxcurr; + } + + ninfo("rxhead: %p rxcurr: %p segments: %d\n", + priv->rxhead, priv->rxcurr, priv->segments); + + /* Check if any errors are reported in the frame */ + + if ((rxdesc->des3 & ETH_RDES3_WB_ES) == 0) + { + struct net_driver_s *dev = &priv->dev; + + /* Get the Frame Length of the received packet: subtract 4 + * bytes of the CRC + */ + + dev->d_len = ((rxdesc->des3 & ETH_RDES3_WB_PL_MASK) >> + ETH_RDES3_WB_PL_SHIFT) - 4; + + /* Get a buffer from the free list. We don't even check if + * this is successful because we already assure the free + * list is not empty above. + */ + + buffer = stm32_allocbuffer(priv); + + /* Take the buffer from the RX descriptor of the first free + * segment, put it into the network device structure, then + * replace the buffer in the RX descriptor with the newly + * allocated buffer. + */ + + DEBUGASSERT(dev->d_buf == NULL); + dev->d_buf = (uint8_t *)rxcurr->des0; + rxcurr->des0 = (uint32_t)buffer; + + /* Make sure that the modified RX descriptor is written to + * physical memory. + */ + + up_clean_dcache((uintptr_t)rxcurr, + (uintptr_t)rxdesc + sizeof(struct eth_desc_s)); + + /* Remember where we should re-start scanning and reset the + * segment scanning logic + */ + + priv->rxhead = stm32_get_next_rxdesc(priv, rxdesc); + stm32_freesegment(priv, rxcurr, priv->segments); + + /* Force the completed RX DMA buffer to be re-read from + * physical memory. + */ + + up_invalidate_dcache((uintptr_t)dev->d_buf, + (uintptr_t)dev->d_buf + dev->d_len); + + ninfo("rxhead: %p d_buf: %p d_len: %d\n", + priv->rxhead, dev->d_buf, dev->d_len); + + /* Return success */ + + return OK; + } + else + { + /* Drop the frame that contains the errors, reset the + * segment scanning logic, and continue scanning with the + * next frame. + */ + + nwarn("WARNING: DROPPED RX descriptor errors: %08x\n", + rxdesc->des3); + stm32_freesegment(priv, rxcurr, priv->segments); + } + } + } + else + { + /* Drop the context descriptors, we are not interested */ + + stm32_freesegment(priv, rxcurr, 1); + } + + /* Try the next descriptor */ + + rxdesc = stm32_get_next_rxdesc(priv, rxdesc); + + /* Force the next RX descriptor to be re-read from physical memory */ + + up_invalidate_dcache((uintptr_t)rxdesc, + (uintptr_t)rxdesc + sizeof(struct eth_desc_s)); + } + + /* We get here after all of the descriptors have been scanned or when + * rxdesc points to the first descriptor owned by the DMA. Remember where + * we left off. + */ + + priv->rxhead = rxdesc; + + ninfo("rxhead: %p rxcurr: %p segments: %d\n", + priv->rxhead, priv->rxcurr, priv->segments); + + return -EAGAIN; +} + +/**************************************************************************** + * Function: stm32_receive + * + * Description: + * An interrupt was received indicating the availability of a new RX packet + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void stm32_receive(struct stm32_ethmac_s *priv) +{ + struct net_driver_s *dev = &priv->dev; + + /* Loop while while stm32_recvframe() successfully retrieves valid + * Ethernet frames. + */ + + while (stm32_recvframe(priv) == OK) + { +#ifdef CONFIG_NET_PKT + /* When packet sockets are enabled, feed the frame into the packet tap */ + + pkt_input(&priv->dev); +#endif + + /* Check if the packet is a valid size for the network buffer + * configuration (this should not happen) + */ + + if (dev->d_len > CONFIG_NET_ETH_PKTSIZE) + { + nwarn("WARNING: DROPPED Too big: %d\n", dev->d_len); + continue; + } + + /* 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"); + + /* Handle ARP on input then give the IPv4 packet to the network + * layer + */ + + arp_ipin(&priv->dev); + ipv4_input(&priv->dev); + + /* If the above function invocation resulted in data that should + * be sent out on the network, the field d_len will set to a + * value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* Update the Ethernet header with the correct MAC address */ + +#ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv4(priv->dev.d_flags)) +#endif + { + arp_out(&priv->dev); + } +#ifdef CONFIG_NET_IPv6 + else + { + neighbor_out(&priv->dev); + } +#endif + + /* And send the packet */ + + stm32_transmit(priv); + } + } + else +#endif +#ifdef CONFIG_NET_IPv6 + if (BUF->type == HTONS(ETHTYPE_IP6)) + { + ninfo("Iv6 frame\n"); + + /* Give the IPv6 packet to the network layer */ + + ipv6_input(&priv->dev); + + /* If the above function invocation resulted in data that should + * be sent out on the network, the field d_len will set to a + * value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* Update the Ethernet header with the correct MAC address */ + +#ifdef CONFIG_NET_IPv4 + if (IFF_IS_IPv4(priv->dev.d_flags)) + { + arp_out(&priv->dev); + } + else +#endif +#ifdef CONFIG_NET_IPv6 + { + neighbor_out(&priv->dev); + } +#endif + + /* And send the packet */ + + stm32_transmit(priv); + } + } + else +#endif +#ifdef CONFIG_NET_ARP + if (BUF->type == htons(ETHTYPE_ARP)) + { + ninfo("ARP frame\n"); + + /* Handle ARP packet */ + + arp_arpin(&priv->dev); + + /* If the above function invocation resulted in data that should + * be sent out on the network, the field d_len will set to a + * value > 0. + */ + + if (priv->dev.d_len > 0) + { + stm32_transmit(priv); + } + } + else +#endif + { + nwarn("WARNING: DROPPED Unknown type: %04x\n", BUF->type); + } + + /* We are finished with the RX buffer. NOTE: If the buffer is + * re-used for transmission, the dev->d_buf field will have been + * nullified. + */ + + if (dev->d_buf) + { + /* Free the receive packet buffer */ + + stm32_freebuffer(priv, dev->d_buf); + dev->d_buf = NULL; + dev->d_len = 0; + } + } +} + +/**************************************************************************** + * Function: stm32_freeframe + * + * Description: + * Scans the TX descriptors and frees the buffers of completed TX transfers. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None. + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void stm32_freeframe(struct stm32_ethmac_s *priv) +{ + struct eth_desc_s *txdesc; + uint32_t des3_tmp; + int i; + + ninfo("txhead: %p txtail: %p inflight: %d\n", + priv->txhead, priv->txtail, priv->inflight); + + /* Scan for "in-flight" descriptors owned by the CPU */ + + txdesc = priv->txtail; + if (txdesc) + { + DEBUGASSERT(priv->inflight > 0); + + /* Force re-reading of the TX descriptor for physical memory */ + + up_invalidate_dcache((uintptr_t)txdesc, + (uintptr_t)txdesc + sizeof(struct eth_desc_s)); + + for (i = 0; (txdesc->des3 & ETH_TDES3_RD_OWN) == 0; i++) + { + /* There should be a buffer assigned to all in-flight + * TX descriptors. + */ + + ninfo("txtail: %p des0: %08x des2: %08x des3: %08x\n", + txdesc, txdesc->des0, txdesc->des2, txdesc->des3); + + DEBUGASSERT(txdesc->des0 != 0); + + /* Check if this is the first segment of a TX frame. */ + + if ((txdesc->des3 & ETH_TDES3_RD_FD) != 0) + { + /* Yes.. Free the buffer */ + + stm32_freebuffer(priv, (uint8_t *)txdesc->des0); + } + + /* In any event, make sure that des0-3 are nullified. */ + + txdesc->des0 = 0; + txdesc->des1 = 0; + txdesc->des2 = 0; + des3_tmp = txdesc->des3; + txdesc->des3 = 0; + + /* Flush the contents of the modified TX descriptor into + * physical memory. + */ + + up_clean_dcache((uintptr_t)txdesc, + (uintptr_t)txdesc + sizeof(struct eth_desc_s)); + + /* Check if this was the last segment of a TX frame */ + + if ((des3_tmp & ETH_TDES3_RD_LD) != 0) + { + /* Yes.. Decrement the number of frames "in-flight". */ + + priv->inflight--; + + /* If all of the TX descriptors were in-flight, then RX + * interruptsmay have been disabled... we can re-enable them + * now. + */ + + stm32_enableint(priv, ETH_DMACIER_RIE); + + /* If there are no more frames in-flight, then bail. */ + + if (priv->inflight <= 0) + { + priv->txtail = NULL; + priv->inflight = 0; + return; + } + } + + /* Try the next descriptor in the TX chain */ + + txdesc = stm32_get_next_txdesc(priv, txdesc); + + /* Force re-reading of the TX descriptor for physical memory */ + + up_invalidate_dcache((uintptr_t)txdesc, + (uintptr_t)txdesc + sizeof(struct eth_desc_s)); + } + + /* We get here if (1) there are still frames "in-flight". Remember + * where we left off. + */ + + priv->txtail = txdesc; + + ninfo("txhead: %p txtail: %p inflight: %d\n", + priv->txhead, priv->txtail, priv->inflight); + } +} + +/**************************************************************************** + * Function: stm32_txdone + * + * Description: + * An interrupt was received indicating that the last TX packet(s) is done + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * + ****************************************************************************/ + +static void stm32_txdone(struct stm32_ethmac_s *priv) +{ + DEBUGASSERT(priv->txtail != NULL); + + /* Scan the TX descriptor change, returning buffers to free list */ + + stm32_freeframe(priv); + + /* If no further xmits are pending, then cancel the TX timeout */ + + if (priv->inflight <= 0) + { + /* Cancel the TX timeout */ + + wd_cancel(priv->txtimeout); + + /* And disable further TX interrupts. */ + + stm32_disableint(priv, ETH_DMACIER_TIE); + } + + /* Then poll the network for new XMIT data */ + + stm32_dopoll(priv); +} + +/**************************************************************************** + * Function: stm32_interrupt_work + * + * Description: + * Perform interrupt related work from the worker thread + * + * Parameters: + * arg - The argument passed when work_queue() was called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +static void stm32_interrupt_work(void *arg) +{ + struct stm32_ethmac_s *priv = (struct stm32_ethmac_s *)arg; + uint32_t dmasr; + + DEBUGASSERT(priv); + + /* Process pending Ethernet interrupts */ + + net_lock(); + + /* Get the DMA interrupt status bits (no MAC interrupts are expected) */ + + dmasr = stm32_getreg(STM32_ETH_DMACSR); + + /* Mask only enabled interrupts. This depends on the fact that the + * interrupt related bits (0-16) correspond in these two registers. + */ + + dmasr &= stm32_getreg(STM32_ETH_DMACIER); + + /* Check if there are pending "normal" interrupts */ + + if ((dmasr & ETH_DMACSR_NIS) != 0) + { + /* Yes.. Check if we received an incoming packet, if so, call + * stm32_receive() + */ + + if ((dmasr & ETH_DMACSR_RI) != 0) + { + /* Clear the pending receive interrupt */ + + stm32_putreg(ETH_DMACSR_RI, STM32_ETH_DMACSR); + + /* Handle the received package */ + + stm32_receive(priv); + } + + /* Check if a packet transmission just completed. If so, call + * stm32_txdone(). This may disable further TX interrupts if there + * are no pending transmissions. + */ + + if ((dmasr & ETH_DMACSR_TI) != 0) + { + /* Clear the pending receive interrupt */ + + stm32_putreg(ETH_DMACSR_TI, STM32_ETH_DMACSR); + + /* Check if there are pending transmissions */ + + stm32_txdone(priv); + } + + /* Clear the pending normal summary interrupt */ + + stm32_putreg(ETH_DMACSR_NIS, STM32_ETH_DMACSR); + } + + /* Handle error interrupt only if CONFIG_DEBUG_NET is eanbled */ + +#ifdef CONFIG_DEBUG_NET + /* Check if there are pending "abnormal" interrupts */ + + if ((dmasr & ETH_DMACSR_AIS) != 0) + { + /* Just let the user know what happened */ + + nerr("ERROR: Abormal event(s): %08x\n", dmasr); + + /* Clear all pending abnormal events */ + + stm32_putreg(ETH_DMAINT_ABNORMAL, STM32_ETH_DMACSR); + + /* Clear the pending abnormal summary interrupt */ + + stm32_putreg(ETH_DMACSR_AIS, STM32_ETH_DMACSR); + } +#endif + + net_unlock(); + + /* Re-enable Ethernet interrupts at the NVIC */ + + up_enable_irq(STM32_IRQ_ETH); +} + +/**************************************************************************** + * Function: stm32_interrupt + * + * Description: + * Hardware interrupt handler + * + * Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info (architecture-specific) + * + * Returned Value: + * OK on success + * + * Assumptions: + * + ****************************************************************************/ + +static int stm32_interrupt(int irq, void *context, FAR void *arg) +{ + struct stm32_ethmac_s *priv = &g_stm32ethmac[0]; + uint32_t dmasr; + + /* Get the DMA interrupt status bits (no MAC interrupts are expected) */ + + dmasr = stm32_getreg(STM32_ETH_DMACSR); + if (dmasr != 0) + { + /* 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(STM32_IRQ_ETH); + + /* Check if a packet transmission just completed. */ + + if ((dmasr & ETH_DMACSR_TI) != 0) + { + /* If a TX transfer just completed, then cancel the TX timeout so + * there will be no race condition between any subsequent timeout + * expiration and the deferred interrupt processing. + */ + + wd_cancel(priv->txtimeout); + } + + DEBUGASSERT(work_available(&priv->irqwork)); + + /* Schedule to perform the interrupt processing on the worker thread. */ + + work_queue(ETHWORK, &priv->irqwork, stm32_interrupt_work, priv, 0); + } + + return OK; +} + +/**************************************************************************** + * Function: stm32_txtimeout_work + * + * Description: + * Perform TX timeout related work from the worker thread + * + * Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +static void stm32_txtimeout_work(void *arg) +{ + struct stm32_ethmac_s *priv = (struct stm32_ethmac_s *)arg; + + /* Reset the hardware. Just take the interface down, then back up again. */ + + net_lock(); + stm32_ifdown(&priv->dev); + stm32_ifup(&priv->dev); + + /* Then poll for new XMIT data */ + + stm32_dopoll(priv); + net_unlock(); +} + +/**************************************************************************** + * Function: stm32_txtimeout_expiry + * + * Description: + * Our TX watchdog timed out. Called from the timer interrupt handler. + * The last TX never completed. Reset the hardware and start again. + * + * Parameters: + * argc - The number of available arguments + * arg - The first argument + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * + ****************************************************************************/ + +static void stm32_txtimeout_expiry(int argc, uint32_t arg, ...) +{ + struct stm32_ethmac_s *priv = (struct stm32_ethmac_s *)arg; + + nerr("ERROR: Timeout!\n"); + + /* 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. + * + * Interrupts will be re-enabled when stm32_ifup() is called. + */ + + up_disable_irq(STM32_IRQ_ETH); + + /* Schedule to perform the TX timeout processing on the worker thread. */ + + DEBUGASSERT(work_available(&priv->irqwork)); + + work_queue(ETHWORK, &priv->irqwork, stm32_txtimeout_work, priv, 0); +} + +/**************************************************************************** + * Function: stm32_poll_work + * + * Description: + * Perform periodic polling from the worker thread + * + * Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +static void stm32_poll_work(void *arg) +{ + struct stm32_ethmac_s *priv = (struct stm32_ethmac_s *)arg; + struct net_driver_s *dev = &priv->dev; + + /* Check if the next TX descriptor is owned by the Ethernet DMA or CPU. We + * cannot perform the timer poll if we are unable to accept another packet + * for transmission. Hmmm.. might be bug here. Does this mean if there is + * a transmit in progress, we will miss TCP time state updates? + * + * In a race condition, ETH_TDES3_OWN may be cleared BUT still not available + * because stm32_freeframe() has not yet run. If stm32_freeframe() has run, + * the buffer1 pointer (des2) will be nullified (and inflight should be < + * CONFIG_STM32H7_ETH_NTXDESC). + */ + + net_lock(); + if ((priv->txhead->des3 & ETH_TDES3_RD_OWN) == 0 && + priv->txhead->des0 == 0) + { + /* If we have the descriptor, then perform the timer poll. Allocate a + * buffer for the poll. + */ + + DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL); + dev->d_buf = stm32_allocbuffer(priv); + + /* We can't poll if we have no buffers */ + + if (dev->d_buf) + { + /* Update TCP timing states and poll the network for new XMIT + * data. + */ + + (void)devif_timer(dev, stm32_txpoll); + + /* We will, most likely end up with a buffer to be freed. But it + * might not be the same one that we allocated above. + */ + + if (dev->d_buf) + { + DEBUGASSERT(dev->d_len == 0); + stm32_freebuffer(priv, dev->d_buf); + dev->d_buf = NULL; + } + } + } + + /* Setup the watchdog poll timer again */ + + (void)wd_start(priv->txpoll, STM32_WDDELAY, stm32_poll_expiry, 1, priv); + net_unlock(); +} + +/**************************************************************************** + * Function: stm32_poll_expiry + * + * Description: + * Periodic timer handler. Called from the timer interrupt handler. + * + * Parameters: + * argc - The number of available arguments + * arg - The first argument + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * + ****************************************************************************/ + +static void stm32_poll_expiry(int argc, uint32_t arg, ...) +{ + struct stm32_ethmac_s *priv = (struct stm32_ethmac_s *)arg; + + /* Schedule to perform the interrupt processing on the worker thread. */ + + if (work_available(&priv->pollwork)) + { + work_queue(ETHWORK, &priv->pollwork, stm32_poll_work, priv, 0); + } + else + { + (void)wd_start(priv->txpoll, STM32_WDDELAY, stm32_poll_expiry, 1, priv); + } +} + +/**************************************************************************** + * Function: stm32_ifup + * + * Description: + * NuttX Callback: Bring up the Ethernet interface when an IP address is + * provided + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int stm32_ifup(struct net_driver_s *dev) +{ + struct stm32_ethmac_s *priv = (struct stm32_ethmac_s *)dev->d_private; + int ret; + +#ifdef CONFIG_NET_IPv4 + ninfo("Bringing up: %d.%d.%d.%d\n", + dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff, + (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24); +#endif +#ifdef CONFIG_NET_IPv6 + ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2], + dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5], + dev->d_ipv6addr[6], dev->d_ipv6addr[7]); +#endif + + /* Configure the Ethernet interface for DMA operation. */ + + ret = stm32_ethconfig(priv); + if (ret < 0) + { + return ret; + } + + /* Set and activate a timer process */ + + (void)wd_start(priv->txpoll, STM32_WDDELAY, stm32_poll_expiry, 1, + (uint32_t)priv); + + /* Enable the Ethernet interrupt */ + + priv->ifup = true; + up_enable_irq(STM32_IRQ_ETH); + + stm32_checksetup(); + return OK; +} + +/**************************************************************************** + * Function: stm32_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int stm32_ifdown(struct net_driver_s *dev) +{ + struct stm32_ethmac_s *priv = (struct stm32_ethmac_s *)dev->d_private; + irqstate_t flags; + + ninfo("Taking the network down\n"); + + /* Disable the Ethernet interrupt */ + + flags = enter_critical_section(); + up_disable_irq(STM32_IRQ_ETH); + + /* Cancel the TX poll timer and TX timeout timers */ + + wd_cancel(priv->txpoll); + wd_cancel(priv->txtimeout); + + /* Put the EMAC in its reset, non-operational state. This should be + * a known configuration that will guarantee the stm32_ifup() always + * successfully brings the interface back up. + */ + + stm32_ethreset(priv); + + /* Mark the device "down" */ + + priv->ifup = false; + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Function: stm32_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + ****************************************************************************/ + +static void stm32_txavail_work(void *arg) +{ + struct stm32_ethmac_s *priv = (struct stm32_ethmac_s *)arg; + + ninfo("ifup: %d\n", priv->ifup); + + /* Ignore the notification if the interface is not yet up */ + + net_lock(); + if (priv->ifup) + { + /* Poll the network for new XMIT data */ + + stm32_dopoll(priv); + } + + net_unlock(); +} + +/**************************************************************************** + * Function: stm32_txavail + * + * Description: + * Driver callback invoked when new TX data is available. This is a + * stimulus perform an out-of-cycle poll and, thereby, reduce the TX + * latency. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called in normal user mode + * + ****************************************************************************/ + +static int stm32_txavail(struct net_driver_s *dev) +{ + struct stm32_ethmac_s *priv = (struct stm32_ethmac_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, stm32_txavail_work, priv, 0); + } + + return OK; +} + +/**************************************************************************** + * Function: stm32_calcethcrc + * + * Description: + * Function to calculate the CRC used by STM32 to check an ethernet frame + * + * Parameters: + * data - the data to be checked + * length - length of the data + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) +static uint32_t stm32_calcethcrc(const uint8_t *data, size_t length) +{ + uint32_t crc = 0xffffffff; + size_t i; + int j; + + for (i = 0; i < length; i++) + { + for (j = 0; j < 8; j++) + { + if (((crc >> 31) ^ (data[i] >> j)) & 0x01) + { + /* x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1 */ + + crc = (crc << 1) ^ 0x04c11db7; + } + else + { + crc = crc << 1; + } + } + } + + return ~crc; +} +#endif + +/**************************************************************************** + * Function: stm32_addmac + * + * Description: + * NuttX Callback: Add the specified MAC address to the hardware multicast + * address filtering + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be added + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) +static int stm32_addmac(struct net_driver_s *dev, const uint8_t *mac) +{ + uint32_t crc; + uint32_t hashindex; + uint32_t temp; + uint32_t registeraddress; + + ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + /* Add the MAC address to the hardware multicast hash table */ + + crc = stm32_calcethcrc(mac, 6); + + hashindex = (crc >> 26) & 0x3f; + + if (hashindex > 31) + { + registeraddress = STM32_ETH_MACHT0R; + hashindex -= 32; + } + else + { + registeraddress = STM32_ETH_MACHT1R; + } + + temp = stm32_getreg(registeraddress); + temp |= 1 << hashindex; + stm32_putreg(temp, registeraddress); + + temp = stm32_getreg(STM32_ETH_MACPFR); + temp |= (ETH_MACPFR_HM | ETH_MACPFR_HPF); + stm32_putreg(temp, STM32_ETH_MACPFR); + + return OK; +} +#endif /* CONFIG_NET_IGMP || CONFIG_NET_ICMPv6 */ + +/**************************************************************************** + * Function: stm32_rmmac + * + * Description: + * NuttX Callback: Remove the specified MAC address from the hardware + * multicast address filtering + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be removed + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NET_IGMP +static int stm32_rmmac(struct net_driver_s *dev, const uint8_t *mac) +{ + uint32_t crc; + uint32_t hashindex; + uint32_t temp; + uint32_t registeraddress; + + ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + /* Remove the MAC address to the hardware multicast hash table */ + + crc = stm32_calcethcrc(mac, 6); + + hashindex = (crc >> 26) & 0x3f; + + if (hashindex > 31) + { + registeraddress = STM32_ETH_MACHT0R; + hashindex -= 32; + } + else + { + registeraddress = STM32_ETH_MACHT1R; + } + + temp = stm32_getreg(registeraddress); + temp &= ~(1 << hashindex); + stm32_putreg(temp, registeraddress); + + /* If there is no address registered any more, delete multicast filtering */ + + if (stm32_getreg(STM32_ETH_MACHT0R) == 0 && + stm32_getreg(STM32_ETH_MACHT1R) == 0) + { + temp = stm32_getreg(STM32_ETH_MACPFR); + temp &= ~(ETH_MACPFR_HM | ETH_MACPFR_HPF); + stm32_putreg(temp, STM32_ETH_MACPFR); + } + + return OK; +} +#endif + +/**************************************************************************** + * Function: stm32_txdescinit + * + * Description: + * Initializes the DMA TX descriptors in chain mode. + * + * Parameters: + * priv - Reference to the driver state structure + * txtable - List of pre-allocated TX descriptors for the Ethernet + * interface + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void stm32_txdescinit(struct stm32_ethmac_s *priv, + union stm32_desc_u *txtable) + +{ + struct eth_desc_s *txdesc; + int i; + + /* priv->txhead will point to the first, available TX descriptor in the + * chain. Set the priv->txhead pointer to the first descriptor in the + * table. + */ + + priv->txhead = &txtable[0].desc; + priv->txchbase = &txtable[0].desc; + + /* priv->txtail will point to the first segment of the oldest pending + * "in-flight" TX transfer. NULL means that there are no active TX + * transfers. + */ + + priv->txtail = NULL; + priv->inflight = 0; + + /* Initialize each TX descriptor */ + + for (i = 0; i < CONFIG_STM32H7_ETH_NTXDESC; i++) + { + txdesc = &txtable[i].desc; + +#ifdef CHECKSUM_BY_HARDWARE +#if 0 + /* Enable the checksum insertion for the TX frames TODO! */ + + txdesc->des0 |= ETH_TDES0_CIC_ALL; +#endif +#endif + + /* Clear Buffer1 address pointer (buffers will be assigned as they + * are used) + */ + + txdesc->des0 = 0; + + /* Clear the rest of the descriptor as well */ + + txdesc->des1 = 0; + txdesc->des2 = 0; + txdesc->des3 = 0; + } + + /* Flush all of the initialized TX descriptors to physical memory */ + + up_clean_dcache((uintptr_t)txtable, + (uintptr_t)txtable + + TXTABLE_SIZE * sizeof(union stm32_desc_u)); + + /* Set Channel Tx descriptor ring length register + * TODO: Why -1 is needed? Without this the ring doesn't wrap around + * properly but the DMACCATXDR advances to outside the descriptor ring + */ + + stm32_putreg(CONFIG_STM32H7_ETH_NTXDESC - 1, STM32_ETH_DMACTXRLR); + + /* Set Transmit Descriptor List Address Register */ + + stm32_putreg((uint32_t)&txtable[0].desc, STM32_ETH_DMACTXDLAR); + + /* Set Transmit Descriptor Tail pointer */ + + stm32_putreg((uint32_t)&txtable[0].desc, STM32_ETH_DMACTXDTPR); +} + +/**************************************************************************** + * Function: stm32_rxdescinit + * + * Description: + * Initializes the DMA RX descriptors in chain mode. + * + * Parameters: + * priv - Reference to the driver state structure + * rxtable - List of pre-allocated RX descriptors for the Ethernet + * interface + * txbuffer - List of pre-allocated DMA buffers for the Ethernet interface + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void stm32_rxdescinit(struct stm32_ethmac_s *priv, + union stm32_desc_u *rxtable, + uint8_t *rxbuffer) +{ + struct eth_desc_s *rxdesc; + int i; + + /* priv->rxhead will point to the first, RX descriptor in the chain. + * This will be where we receive the first incomplete frame. + */ + + priv->rxhead = &rxtable[0].desc; + priv->rxchbase = &rxtable[0].desc; + + /* If we accumulate the frame in segments, priv->rxcurr points to the + * RX descriptor of the first segment in the current TX frame. + */ + + priv->rxcurr = NULL; + priv->segments = 0; + + /* Initialize each RX descriptor */ + + for (i = 0; i < CONFIG_STM32H7_ETH_NRXDESC; i++) + { + rxdesc = &rxtable[i].desc; + + /* Set Buffer1 address pointer */ + + rxdesc->des0 = (uint32_t)&rxbuffer[i * ALIGNED_BUFSIZE]; + + /* Set Buffer1 address high bytes */ + + rxdesc->des1 = 0; + + /* Set Buffer2 address high bytes */ + + rxdesc->des2 = 0; + + /* Set Own bit of the RX descriptor des3 and buffer address valid */ + + rxdesc->des3 = ETH_RDES3_RD_OWN | ETH_RDES3_RD_IOC | + ETH_RDES3_RD_BUF1V; + } + + /* Flush all of the initialized RX descriptors to physical memory */ + + up_clean_dcache((uintptr_t)rxtable, + (uintptr_t)rxtable + + RXTABLE_SIZE * sizeof(union stm32_desc_u)); + + /* Set Receive Descriptor ring length register + * TODO: Why -1 is needed? Without this the ring doesn't wrap around + * properly but the DMACCARXDR advances to outside the descriptor ring + */ + + stm32_putreg(CONFIG_STM32H7_ETH_NRXDESC - 1, STM32_ETH_DMACRXRLR); + + /* Set Receive Descriptor List Address Register */ + + stm32_putreg((uint32_t)&rxtable[0].desc, STM32_ETH_DMACRXDLAR); + + /* Set Receive Descriptor Tail pointer Address */ + + stm32_putreg((uint32_t)&rxtable[CONFIG_STM32H7_ETH_NRXDESC - 1].desc, + STM32_ETH_DMACRXDTPR); +} + +/**************************************************************************** + * Function: stm32_ioctl + * + * Description: + * Executes the SIOCxMIIxxx command and responds using the request struct + * that must be provided as its 2nd parameter. + * + * When called with SIOCGMIIPHY it will get the PHY address for the device + * and write it to the req->phy_id field of the request struct. + * + * When called with SIOCGMIIREG it will read a register of the PHY that is + * specified using the req->reg_no struct field and then write its output + * to the req->val_out field. + * + * When called with SIOCSMIIREG it will write to a register of the PHY that + * is specified using the req->reg_no struct field and use req->val_in as + * its input. + * + * Parameters: + * dev - Ethernet device structure + * cmd - SIOCxMIIxxx command code + * arg - Request structure also used to return values + * + * Returned Value: Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_PHY_IOCTL +static int stm32_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg) +{ +#ifdef CONFIG_ARCH_PHY_INTERRUPT + struct stm32_ethmac_s *priv = (struct stm32_ethmac_s *)dev->d_private; +#endif + int ret; + + switch (cmd) + { +#ifdef CONFIG_ARCH_PHY_INTERRUPT + case SIOCMIINOTIFY: /* Set up for PHY event notifications */ + { + struct mii_iotcl_notify_s *req = + (struct mii_iotcl_notify_s *)((uintptr_t)arg); + + ret = phy_notify_subscribe(dev->d_ifname, req->pid, req->signo, + req->arg); + if (ret == OK) + { + /* Enable PHY link up/down interrupts */ + + ret = stm32_phyintenable(priv); + } + } + break; +#endif + + case SIOCGMIIPHY: /* Get MII PHY address */ + { + struct mii_ioctl_data_s *req = + (struct mii_ioctl_data_s *)((uintptr_t)arg); + req->phy_id = CONFIG_STM32H7_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); + ret = stm32_phyread(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); + ret = stm32_phywrite(req->phy_id, req->reg_num, req->val_in, + 0xffff); + } + break; + + default: + ret = -ENOTTY; + break; + } + + return ret; +} +#endif /* CONFIG_NETDEV_PHY_IOCTL */ + +/**************************************************************************** + * Function: stm32_phyintenable + * + * Description: + * Enable link up/down PHY interrupts. The interrupt protocol is like this: + * + * - Interrupt status is cleared when the interrupt is enabled. + * - Interrupt occurs. Interrupt is disabled (at the processor level) when + * is received. + * - Interrupt status is cleared when the interrupt is re-enabled. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno (-ETIMEDOUT) on failure. + * + ****************************************************************************/ + +#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT) +static int stm32_phyintenable(struct stm32_ethmac_s *priv) +{ +#warning Missing logic + return -ENOSYS; +} +#endif + +/**************************************************************************** + * Function: stm32_phyread + * + * Description: + * Read a PHY register. + * + * Parameters: + * phydevaddr - The PHY device address + * phyregaddr - The PHY register address + * value - The location to return the 16-bit PHY register value. + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int stm32_phyread(uint16_t phydevaddr, uint16_t phyregaddr, + uint16_t *value) +{ + volatile uint32_t timeout; + uint32_t regval; + + /* Configure the MACMDIOAR register, preserving CSR Clock Range CR[2:0] bits */ + + regval = stm32_getreg(STM32_ETH_MACMDIOAR); + regval &= ETH_MACMDIOAR_CR_MASK; + + /* Set the PHY device address, PHY register address, and set the buy bit. + * the ETH_MACMDIOAR_GOC == 3, indicating a read operation. + */ + + regval |= (((uint32_t)phydevaddr << ETH_MACMDIOAR_PA_SHIFT) & + ETH_MACMDIOAR_PA_MASK); + regval |= (((uint32_t)phyregaddr << ETH_MACMDIOAR_RDA_SHIFT) & + ETH_MACMDIOAR_RDA_MASK); + regval |= ETH_MACMDIOAR_MB | ETH_MACMDIOAR_GOC_READ; + + stm32_putreg(regval, STM32_ETH_MACMDIOAR); + + /* Wait for the transfer to complete */ + + for (timeout = 0; timeout < PHY_READ_TIMEOUT; timeout++) + { + if ((stm32_getreg(STM32_ETH_MACMDIOAR) & ETH_MACMDIOAR_MB) == 0) + { + *value = (uint16_t)stm32_getreg(STM32_ETH_MACMDIODR); + return OK; + } + } + + ninfo("MII transfer timed out: phydevaddr: %04x phyregaddr: %04x\n", + phydevaddr, phyregaddr); + + return -ETIMEDOUT; +} + +/**************************************************************************** + * Function: stm32_phywrite + * + * Description: + * Write to a PHY register. + * + * Parameters: + * phydevaddr - The PHY device address + * phyregaddr - The PHY register address + * value - The 16-bit value to write to the PHY register value. + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int stm32_phywrite(uint16_t phydevaddr, uint16_t phyregaddr, + uint16_t set, uint16_t clear) +{ + volatile uint32_t timeout; + uint32_t regval; + uint16_t value; + + /* Configure the MACMDIOAR register, preserving CSR Clock Range CR[2:0] bits */ + + regval = stm32_getreg(STM32_ETH_MACMDIOAR); + regval &= ETH_MACMDIOAR_CR_MASK; + + /* Read the existing register value, if clear mask is given */ + + if (clear != 0xffff) + { + if (stm32_phyread(phydevaddr, phyregaddr, &value) != OK) + { + return -ETIMEDOUT; + } + + value &= ~clear; + value |= set; + } + else + { + value = set; + } + + /* Set the PHY device address, PHY register address, and set the busy bit. + * the ETH_MACMDIOAR_GOC == 1, indicating a write operation. + */ + + regval |= (((uint32_t)phydevaddr << ETH_MACMDIOAR_PA_SHIFT) & + ETH_MACMDIOAR_PA_MASK); + regval |= (((uint32_t)phyregaddr << ETH_MACMDIOAR_RDA_SHIFT) & + ETH_MACMDIOAR_RDA_MASK); + regval |= (ETH_MACMDIOAR_MB | ETH_MACMDIOAR_GOC_WRITE); + + /* Write the value into the MACIIDR register before setting the new + * MACMDIOAR register value. + */ + + stm32_putreg(value, STM32_ETH_MACMDIODR); + stm32_putreg(regval, STM32_ETH_MACMDIOAR); + + /* Wait for the transfer to complete */ + + for (timeout = 0; timeout < PHY_WRITE_TIMEOUT; timeout++) + { + if ((stm32_getreg(STM32_ETH_MACMDIOAR) & ETH_MACMDIOAR_MB) == 0) + { + return OK; + } + } + + ninfo("MII transfer timed out: phydevaddr: %04x phyregaddr: %04x value: %04x\n", + phydevaddr, phyregaddr, value); + + return -ETIMEDOUT; +} + +/**************************************************************************** + * Function: stm32_dm9161 + * + * Description: + * Special workaround for the Davicom DM9161 PHY is required. On power, + * up, the PHY is not usually configured correctly but will work after + * a powered-up reset. This is really a workaround for some more + * fundamental issue with the PHY clocking initialization, but the + * root cause has not been studied (nor will it be with this workaround). + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_ETH0_PHY_DM9161 +static inline int stm32_dm9161(struct stm32_ethmac_s *priv) +{ + uint16_t phyval; + int ret; + + /* Read the PHYID1 register; A failure to read the PHY ID is one + * indication that check if the DM9161 PHY CHIP is not ready. + */ + + ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, MII_PHYID1, &phyval); + if (ret < 0) + { + nerr("ERROR: Failed to read the PHY ID1: %d\n", ret); + return ret; + } + + /* If we failed to read the PHY ID1 register, the reset the MCU to recover */ + + else if (phyval == 0xffff) + { + up_systemreset(); + } + + ninfo("PHY ID1: 0x%04X\n", phyval); + + /* Now check the "DAVICOM Specified Configuration Register (DSCR)", Register 16 */ + + ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, 16, &phyval); + if (ret < 0) + { + nerr("ERROR: Failed to read the PHY Register 0x10: %d\n", ret); + return ret; + } + + /* Bit 8 of the DSCR register is zero, then the DM9161 has not selected + * RMII. If RMII is not selected, then reset the MCU to recover. + */ + + else if ((phyval & (1 << 8)) == 0) + { + up_systemreset(); + } + + return OK; +} +#endif + +/**************************************************************************** + * Function: stm32_phyregdump + * + * Description: + * Dump the MII registers 0-31 + * + * Parameters: + * + * Returned Value: + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_ETHMAC_REGDEBUG +static void stm32_phyregdump() +{ + uint16_t phyval; + int ret; + int i; + + for (i = 0; i < 0x20; i++) + { + ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, i, &phyval); + if (ret < 0) + { + nerr("ERROR: Failed to read reg: 0%2x\n", i); + } + else + { + ninfo("Phy reg 0x%02x == 0x%x\n", i, phyval); + } + } +} +#endif + +/**************************************************************************** + * Function: stm32_phyinit + * + * Description: + * Configure the PHY and determine the link speed/duplex. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int stm32_phyinit(struct stm32_ethmac_s *priv) +{ +#ifdef CONFIG_STM32H7_AUTONEG + volatile uint32_t timeout; +#endif + uint32_t regval; + uint16_t phyval; + int ret; + int to; + + /* Assume 10MBps and half duplex */ + + priv->mbps100 = 0; + priv->fduplex = 0; + + /* Setup up PHY clocking by setting the SR field in the MACMDIOAR register */ + + regval = stm32_getreg(STM32_ETH_MACMDIOAR); + regval &= ~ETH_MACMDIOAR_CR_MASK; + regval |= ETH_MACMDIOAR_CR; + stm32_putreg(regval, STM32_ETH_MACMDIOAR); + + /* Put the PHY in reset mode */ + + ret = stm32_phywrite(CONFIG_STM32H7_PHYADDR, MII_MCR, MII_MCR_RESET, + MII_MCR_RESET); + if (ret < 0) + { + nerr("ERROR: Failed to reset the PHY: %d\n", ret); + return ret; + } + + to = PHY_RESET_DELAY; + do + { + up_mdelay(10); + to -= 10; + ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, MII_MCR, &phyval); + } + while (phyval & MII_MCR_RESET && to > 0); + + if (to <= 0) + { + nerr("ERROR: Phy reset timeout\n"); + return ret; + } + else + { + ninfo("Phy reset in %d ms\n", PHY_RESET_DELAY - to); + } + +#ifdef CONFIG_STM32H7_ETHMAC_REGDEBUG + stm32_phyregdump(); +#endif + + /* Perform any necessary, board-specific PHY initialization */ + +#ifdef CONFIG_STM32H7_PHYINIT + ret = stm32_phy_boardinitialize(0); + if (ret < 0) + { + nerr("ERROR: Failed to initialize the PHY: %d\n", ret); + return ret; + } +#endif + + /* Special workaround for the Davicom DM9161 PHY is required. */ + +#ifdef CONFIG_ETH0_PHY_DM9161 + ret = stm32_dm9161(priv); + if (ret < 0) + { + return ret; + } +#endif + + /* Perform auto-negotiation if so configured */ + +#ifdef CONFIG_STM32H7_AUTONEG + /* Wait for link status */ + + for (timeout = 0; timeout < PHY_RETRY_TIMEOUT; timeout++) + { + ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, MII_MSR, &phyval); + if (ret < 0) + { + nerr("ERROR: Failed to read the PHY MSR: %d\n", ret); + return ret; + } + else if ((phyval & MII_MSR_LINKSTATUS) != 0) + { + break; + } + } + + if (timeout >= PHY_RETRY_TIMEOUT) + { + nerr("ERROR: Timed out waiting for link status: %04x\n", phyval); + return -ETIMEDOUT; + } + + /* Enable auto-negotiation */ + + ret = stm32_phywrite(CONFIG_STM32H7_PHYADDR, MII_MCR, MII_MCR_ANENABLE, + MII_MCR_ANENABLE); + if (ret < 0) + { + nerr("ERROR: Failed to enable auto-negotiation: %d\n", ret); + return ret; + } + + /* Wait until auto-negotiation completes */ + + for (timeout = 0; timeout < PHY_RETRY_TIMEOUT; timeout++) + { + ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, MII_MSR, &phyval); + if (ret < 0) + { + nerr("ERROR: Failed to read the PHY MSR: %d\n", ret); + return ret; + } + else if ((phyval & MII_MSR_ANEGCOMPLETE) != 0) + { + break; + } + } + + if (timeout >= PHY_RETRY_TIMEOUT) + { + nerr("ERROR: Timed out waiting for auto-negotiation\n"); + return -ETIMEDOUT; + } + + /* Read the result of the auto-negotiation from the PHY-specific register */ + + ret = stm32_phyread(CONFIG_STM32H7_PHYADDR, CONFIG_STM32H7_PHYSR, &phyval); + if (ret < 0) + { + nerr("ERROR: Failed to read PHY status register\n"); + return ret; + } + + /* Remember the selected speed and duplex modes */ + + ninfo("PHYSR[%d]: %04x\n", CONFIG_STM32H7_PHYSR, phyval); + + /* Different PHYs present speed and mode information in different ways. IF + * This CONFIG_STM32H7_PHYSR_ALTCONFIG is selected, this indicates that + * the PHY represents speed and mode information are combined, for + * example, with separate bits for 10HD, 100HD, 10FD and 100FD. + */ + +#ifdef CONFIG_STM32H7_PHYSR_ALTCONFIG + switch (phyval & CONFIG_STM32H7_PHYSR_ALTMODE) + { + default: + nerr("ERROR: Unrecognized PHY status setting\n"); + + /* Falls through */ + + case CONFIG_STM32H7_PHYSR_10HD: + priv->fduplex = 0; + priv->mbps100 = 0; + break; + + case CONFIG_STM32H7_PHYSR_100HD: + priv->fduplex = 0; + priv->mbps100 = 1; + break; + + case CONFIG_STM32H7_PHYSR_10FD: + priv->fduplex = 1; + priv->mbps100 = 0; + break; + + case CONFIG_STM32H7_PHYSR_100FD: + priv->fduplex = 1; + priv->mbps100 = 1; + break; + } + + /* Different PHYs present speed and mode information in different ways. + * Some will present separate information for speed and mode (this is the + * default). Those PHYs, for example, may provide a 10/100 Mbps + * indication and a separate full/half duplex indication. + */ + +#else + if ((phyval & CONFIG_STM32H7_PHYSR_MODE) == CONFIG_STM32H7_PHYSR_FULLDUPLEX) + { + priv->fduplex = 1; + } + + if ((phyval & CONFIG_STM32H7_PHYSR_SPEED) == CONFIG_STM32H7_PHYSR_100MBPS) + { + priv->mbps100 = 1; + } +#endif + +#else /* Auto-negotion not selected */ + + phyval = 0; +#ifdef CONFIG_STM32H7_ETHFD + phyval |= MII_MCR_FULLDPLX; +#endif +#ifdef CONFIG_STM32H7_ETH100MBPS + phyval |= MII_MCR_SPEED100; +#endif + + ret = stm32_phywrite(CONFIG_STM32H7_PHYADDR, MII_MCR, phyval, 0xffff); + if (ret < 0) + { + nerr("ERROR: Failed to write the PHY MCR: %d\n", ret); + return ret; + } + + up_mdelay(PHY_CONFIG_DELAY); + + /* Remember the selected speed and duplex modes */ + +#ifdef CONFIG_STM32H7_ETHFD + priv->fduplex = 1; +#endif +#ifdef CONFIG_STM32H7_ETH100MBPS + priv->mbps100 = 1; +#endif +#endif + + ninfo("Duplex: %s Speed: %d MBps\n", + priv->fduplex ? "FULL" : "HALF", + priv->mbps100 ? 100 : 10); + + return OK; +} + +/**************************************************************************** + * Name: stm32_selectmii + * + * Description: + * Selects the MII inteface. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_MII +static inline void stm32_selectmii(void) +{ + uint32_t regval; + + regval = getreg32(STM32_SYSCFG_PMC); + regval &= ~SYSCFG_PMC_EPIS_MASK; + regval |= SYSCFG_PMC_EPIS_MII; + putreg32(regval, STM32_SYSCFG_PMC); +} +#endif + +/**************************************************************************** + * Name: stm32_selectrmii + * + * Description: + * Selects the RMII inteface. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void stm32_selectrmii(void) +{ + uint32_t regval; + + regval = getreg32(STM32_SYSCFG_PMC); + regval &= ~SYSCFG_PMC_EPIS_MASK; + regval |= SYSCFG_PMC_EPIS_RMII; + putreg32(regval, STM32_SYSCFG_PMC); +} + +/**************************************************************************** + * Function: stm32_ethgpioconfig + * + * Description: + * Configure GPIOs for the Ethernet interface. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None. + * + * Assumptions: + * + ****************************************************************************/ + +static inline void stm32_ethgpioconfig(struct stm32_ethmac_s *priv) +{ + /* Configure GPIO pins to support Ethernet */ + +#if defined(CONFIG_STM32H7_MII) || defined(CONFIG_STM32H7_RMII) + + /* MDC and MDIO are common to both modes */ + + stm32_configgpio(GPIO_ETH_MDC); + stm32_configgpio(GPIO_ETH_MDIO); + + /* Set up the MII interface */ + +#if defined(CONFIG_STM32H7_MII) + + /* Select the MII interface */ + + stm32_selectmii(); + + /* Provide clocking via MCO, MCO1 or MCO2: + * + * "MCO1 (microcontroller clock output), used to output HSI, LSE, HSE or PLL + * clock (through a configurable prescaler) on PA8 pin." + * + * "MCO2 (microcontroller clock output), used to output HSE, PLL, SYSCLK or + * PLLI2S clock (through a configurable prescaler) on PC9 pin." + */ + +# if defined(CONFIG_STM32H7_MII_MCO1) + /* Configure MC01 to drive the PHY. Board logic must provide MC01 clocking + * info. + */ + + stm32_configgpio(GPIO_MCO1); + stm32_mco1config(BOARD_CFGR_MC01_SOURCE, BOARD_CFGR_MC01_DIVIDER); + +# elif defined(CONFIG_STM32H7_MII_MCO2) + /* Configure MC02 to drive the PHY. Board logic must provide MC02 clocking + * info. + */ + + stm32_configgpio(GPIO_MCO2); + stm32_mco2config(BOARD_CFGR_MC02_SOURCE, BOARD_CFGR_MC02_DIVIDER); + +# elif defined(CONFIG_STM32H7_MII_MCO) + /* Setup MCO pin for alternative usage */ + + stm32_configgpio(GPIO_MCO); + stm32_mcoconfig(BOARD_CFGR_MCO_SOURCE); +# endif + + /* MII interface pins (17): + * + * MII_TX_CLK, MII_TXD[3:0], MII_TX_EN, MII_RX_CLK, MII_RXD[3:0], MII_RX_ER, + * MII_RX_DV, MII_CRS, MII_COL, MDC, MDIO + */ + + stm32_configgpio(GPIO_ETH_MII_COL); + stm32_configgpio(GPIO_ETH_MII_CRS); + stm32_configgpio(GPIO_ETH_MII_RXD0); + stm32_configgpio(GPIO_ETH_MII_RXD1); + stm32_configgpio(GPIO_ETH_MII_RXD2); + stm32_configgpio(GPIO_ETH_MII_RXD3); + stm32_configgpio(GPIO_ETH_MII_RX_CLK); + stm32_configgpio(GPIO_ETH_MII_RX_DV); + stm32_configgpio(GPIO_ETH_MII_RX_ER); + stm32_configgpio(GPIO_ETH_MII_TXD0); + stm32_configgpio(GPIO_ETH_MII_TXD1); + stm32_configgpio(GPIO_ETH_MII_TXD2); + stm32_configgpio(GPIO_ETH_MII_TXD3); + stm32_configgpio(GPIO_ETH_MII_TX_CLK); + stm32_configgpio(GPIO_ETH_MII_TX_EN); + + /* Set up the RMII interface. */ + +#elif defined(CONFIG_STM32H7_RMII) + + /* Select the RMII interface */ + + stm32_selectrmii(); + + /* Provide clocking via MCO, MCO1 or MCO2: + * + * "MCO1 (microcontroller clock output), used to output HSI, LSE, HSE or PLL + * clock (through a configurable prescaler) on PA8 pin." + * + * "MCO2 (microcontroller clock output), used to output HSE, PLL, SYSCLK or + * PLLI2S clock (through a configurable prescaler) on PC9 pin." + */ + +# if defined(CONFIG_STM32H7_RMII_MCO1) + /* Configure MC01 to drive the PHY. Board logic must provide MC01 clocking + * info. + */ + + stm32_configgpio(GPIO_MCO1); + stm32_mco1config(BOARD_CFGR_MC01_SOURCE, BOARD_CFGR_MC01_DIVIDER); + +# elif defined(CONFIG_STM32H7_RMII_MCO2) + /* Configure MC02 to drive the PHY. Board logic must provide MC02 clocking + * info. + */ + + stm32_configgpio(GPIO_MCO2); + stm32_mco2config(BOARD_CFGR_MC02_SOURCE, BOARD_CFGR_MC02_DIVIDER); + +# elif defined(CONFIG_STM32H7_RMII_MCO) + /* Setup MCO pin for alternative usage */ + + stm32_configgpio(GPIO_MCO); + stm32_mcoconfig(BOARD_CFGR_MCO_SOURCE); +# endif + + /* RMII interface pins (7): + * + * RMII_TXD[1:0], RMII_TX_EN, RMII_RXD[1:0], RMII_CRS_DV, MDC, MDIO, + * RMII_REF_CLK + */ + + stm32_configgpio(GPIO_ETH_RMII_CRS_DV); + stm32_configgpio(GPIO_ETH_RMII_REF_CLK); + stm32_configgpio(GPIO_ETH_RMII_RXD0); + stm32_configgpio(GPIO_ETH_RMII_RXD1); + stm32_configgpio(GPIO_ETH_RMII_TXD0); + stm32_configgpio(GPIO_ETH_RMII_TXD1); + stm32_configgpio(GPIO_ETH_RMII_TX_EN); +#endif +#endif + +#ifdef CONFIG_STM32H7_ETH_PTP + /* Enable pulse-per-second (PPS) output signal */ + + stm32_configgpio(GPIO_ETH_PPS_OUT); +#endif +} + +/**************************************************************************** + * Function: stm32_ethreset + * + * Description: + * Reset the Ethernet block. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None. + * + * Assumptions: + * + ****************************************************************************/ + +static void stm32_ethreset(struct stm32_ethmac_s *priv) +{ + uint32_t regval; + + /* Reset the Ethernet on the AHB1 bus */ + + regval = stm32_getreg(STM32_RCC_AHB1RSTR); + regval |= RCC_AHB1RSTR_ETH1MACRST; + stm32_putreg(regval, STM32_RCC_AHB1RSTR); + + regval &= ~RCC_AHB1RSTR_ETH1MACRST; + stm32_putreg(regval, STM32_RCC_AHB1RSTR); + + /* Perform a software reset by setting the SR bit in the DMAMR register. + * This Resets all MAC subsystem internal registers and logic. After this + * reset all the registers holds their reset values. + */ + + regval = stm32_getreg(STM32_ETH_DMAMR); + regval |= ETH_DMAMR_SWR; + stm32_putreg(regval, STM32_ETH_DMAMR); + + /* Wait for software reset to complete. The SR bit is cleared automatically + * after the reset operation has completed in all of the core clock domains. + */ + + while ((stm32_getreg(STM32_ETH_DMAMR) & ETH_DMAMR_SWR) != 0); + + /* Acording to the spec, these need to be done before creating + * the descriptor lists, so initialize these already here + */ + + /* Set up the DMAMR register */ + + regval = stm32_getreg(STM32_ETH_DMAMR); + regval &= ~DMAMR_CLEAR_MASK; + regval |= DMAMR_SET_MASK; + stm32_putreg(regval, STM32_ETH_DMAMR); + + /* Set up the DMASBMR register */ + + regval = stm32_getreg(STM32_ETH_DMASBMR); + regval &= ~DMASBMR_CLEAR_MASK; + regval |= DMASBMR_SET_MASK; + stm32_putreg(regval, STM32_ETH_DMASBMR); +} + +/**************************************************************************** + * Function: stm32_macconfig + * + * Description: + * Configure the Ethernet MAC for DMA operation. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int stm32_macconfig(struct stm32_ethmac_s *priv) +{ + uint32_t regval; + + /* DMA Configuration */ + + /* Set up the ETH_DMACCR register */ + + regval = stm32_getreg(STM32_ETH_DMACCR); + regval &= ~DMACCR_CLEAR_MASK; + regval |= DMACCR_SET_MASK; + stm32_putreg(regval, STM32_ETH_DMACCR); + + /* Set up the ETH_DMACTXCR register */ + + regval = stm32_getreg(STM32_ETH_DMACTXCR); + regval &= ~DMACTXCR_CLEAR_MASK; + regval |= DMACTXCR_SET_MASK; + stm32_putreg(regval, STM32_ETH_DMACTXCR); + + /* Set up the ETH_DMACRXCR register */ + + regval = stm32_getreg(STM32_ETH_DMACRXCR); + regval &= ~DMACRXCR_CLEAR_MASK; + regval |= DMACRXCR_SET_MASK; + stm32_putreg(regval, STM32_ETH_DMACRXCR); + + /* MTL configuration */ + + /* Set up the MTLTXQOMR register */ + + regval = stm32_getreg(STM32_ETH_MTLTXQOMR); + regval &= ~MTLTXQOMR_CLEAR_MASK; + regval |= MTLTXQOMR_SET_MASK; + stm32_putreg(regval, STM32_ETH_MTLTXQOMR); + + /* Set up the MTLRXQOMR register */ + + regval = stm32_getreg(STM32_ETH_MTLRXQOMR); + regval &= ~MTLRXQOMR_CLEAR_MASK; + regval |= MTLRXQOMR_SET_MASK; + stm32_putreg(regval, STM32_ETH_MTLRXQOMR); + + /* MAC Configuration */ + + /* Set up the MACCR register */ + + regval = stm32_getreg(STM32_ETH_MACCR); + regval &= ~MACCR_CLEAR_BITS; + regval |= MACCR_SET_BITS; + + if (priv->fduplex) + { + /* Set the DM bit for full duplex support */ + + regval |= ETH_MACCR_DM; + } + + if (priv->mbps100) + { + /* Set the FES bit for 100Mbps fast ethernet support */ + + regval |= ETH_MACCR_FES; + } + + stm32_putreg(regval, STM32_ETH_MACCR); + + /* Set up the MACPFR register */ + + regval = stm32_getreg(STM32_ETH_MACPFR); + regval &= ~MACPFR_CLEAR_BITS; + regval |= MACPFR_SET_BITS; + stm32_putreg(regval, STM32_ETH_MACPFR); + + /* Set up the MACHT0R and MACHT1R registers */ + + stm32_putreg(0, STM32_ETH_MACHT0R); + stm32_putreg(0, STM32_ETH_MACHT1R); + + /* Setup up the MACQTXFCR register */ + + regval = stm32_getreg(STM32_ETH_MACQTXFCR); + regval &= ~MACQTXFCR_CLEAR_MASK; + regval |= MACQTXFCR_SET_MASK; + stm32_putreg(regval, STM32_ETH_MACQTXFCR); + + /* Setup up the MACRXFCR register */ + + regval = stm32_getreg(STM32_ETH_MACRXFCR); + regval &= ~MACRXFCR_CLEAR_MASK; + regval |= MACRXFCR_SET_MASK; + stm32_putreg(regval, STM32_ETH_MACRXFCR); + + /* Setup up the MACVTR register */ + + stm32_putreg(0, STM32_ETH_MACVTR); + return OK; +} + +/**************************************************************************** + * Function: stm32_macaddress + * + * Description: + * Configure the selected MAC address. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static void stm32_macaddress(struct stm32_ethmac_s *priv) +{ + struct net_driver_s *dev = &priv->dev; + uint32_t regval; + + ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->d_ifname, + dev->d_mac.ether.ether_addr_octet[0], + dev->d_mac.ether.ether_addr_octet[1], + dev->d_mac.ether.ether_addr_octet[2], + dev->d_mac.ether.ether_addr_octet[3], + dev->d_mac.ether.ether_addr_octet[4], + dev->d_mac.ether.ether_addr_octet[5]); + + /* Set the MAC address high register */ + + regval = ((uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8) | + (uint32_t)dev->d_mac.ether.ether_addr_octet[4]; + stm32_putreg(regval, STM32_ETH_MACA0HR); + + /* Set the MAC address low register */ + + regval = ((uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24) | + ((uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16) | + ((uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8) | + (uint32_t)dev->d_mac.ether.ether_addr_octet[0]; + stm32_putreg(regval, STM32_ETH_MACA0LR); +} + +/**************************************************************************** + * Function: stm32_ipv6multicast + * + * Description: + * Configure the IPv6 multicast MAC address. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NET_ICMPv6 +static void stm32_ipv6multicast(struct stm32_ethmac_s *priv) +{ + struct net_driver_s *dev; + uint16_t tmp16; + uint8_t mac[6]; + + /* For ICMPv6, we need to add the IPv6 multicast address + * + * For IPv6 multicast addresses, the Ethernet MAC is derived by + * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00, + * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map + * to the Ethernet MAC address 33:33:00:01:00:03. + * + * NOTES: This appears correct for the ICMPv6 Router Solicitation + * Message, but the ICMPv6 Neighbor Solicitation message seems to + * use 33:33:ff:01:00:03. + */ + + mac[0] = 0x33; + mac[1] = 0x33; + + dev = &priv->dev; + tmp16 = dev->d_ipv6addr[6]; + mac[2] = 0xff; + mac[3] = tmp16 >> 8; + + tmp16 = dev->d_ipv6addr[7]; + mac[4] = tmp16 & 0xff; + mac[5] = tmp16 >> 8; + + ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + (void)stm32_addmac(dev, mac); + +#ifdef CONFIG_NET_ICMPv6_AUTOCONF + /* Add the IPv6 all link-local nodes Ethernet address. This is the + * address that we expect to receive ICMPv6 Router Advertisement + * packets. + */ + + (void)stm32_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet); + +#endif /* CONFIG_NET_ICMPv6_AUTOCONF */ +#ifdef CONFIG_NET_ICMPv6_ROUTER + /* Add the IPv6 all link-local routers Ethernet address. This is the + * address that we expect to receive ICMPv6 Router Solicitation + * packets. + */ + + (void)stm32_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet); + +#endif /* CONFIG_NET_ICMPv6_ROUTER */ +} +#endif /* CONFIG_NET_ICMPv6 */ + +/**************************************************************************** + * Function: stm32_macenable + * + * Description: + * Enable normal MAC operation. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int stm32_macenable(struct stm32_ethmac_s *priv) +{ + uint32_t regval; + + /* Set the MAC address */ + + stm32_macaddress(priv); + +#ifdef CONFIG_NET_ICMPv6 + /* Set up the IPv6 multicast address */ + + stm32_ipv6multicast(priv); +#endif + + /* Enable transmit state machine of the MAC for transmission on the MII */ + + regval = stm32_getreg(STM32_ETH_MACCR); + regval |= ETH_MACCR_TE; + stm32_putreg(regval, STM32_ETH_MACCR); + + /* Flush Transmit FIFO */ + + regval = stm32_getreg(STM32_ETH_MTLTXQOMR); + regval |= ETH_MTLTXQOMR_FTQ; + stm32_putreg(regval, STM32_ETH_MTLTXQOMR); + + /* Enable receive state machine of the MAC for reception from the MII */ + + /* Enables or disables the MAC reception. */ + + regval = stm32_getreg(STM32_ETH_MACCR); + regval |= ETH_MACCR_RE; + stm32_putreg(regval, STM32_ETH_MACCR); + + /* Start DMA transmission */ + + regval = stm32_getreg(STM32_ETH_DMACTXCR); + regval |= ETH_DMACTXCR_ST; + stm32_putreg(regval, STM32_ETH_DMACTXCR); + + /* Start DMA reception */ + + regval = stm32_getreg(STM32_ETH_DMACRXCR); + regval |= ETH_DMACRXCR_SR; + stm32_putreg(regval, STM32_ETH_DMACRXCR); + + /* Enable Ethernet DMA interrupts */ + + stm32_putreg(ETH_MACIER_ALLINTS, STM32_ETH_MACIER); + + /* Ethernet DMA supports two classes of interrupts: Normal interrupt + * summary (NIS) and Abnormal interrupt summary (AIS) with a variety + * individual normal and abnormal interrupting events. Here only + * the normal receive event is enabled (unless DEBUG is enabled). Transmit + * events will only be enabled when a transmit interrupt is expected. + */ + + stm32_putreg((ETH_DMAINT_RECV_ENABLE | ETH_DMAINT_XMIT_ENABLE | + ETH_DMAINT_ERROR_ENABLE), STM32_ETH_DMACIER); + + /* Clear Tx and Rx process stopped flags */ + + regval = stm32_getreg(STM32_ETH_DMACRXCR); + regval |= (ETH_DMACSR_TPS | ETH_DMACSR_RPS); + stm32_putreg(regval, STM32_ETH_DMACRXCR); + + return OK; +} + +/**************************************************************************** + * Function: stm32_ethconfig + * + * Description: + * Configure the Ethernet interface for DMA operation. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int stm32_ethconfig(struct stm32_ethmac_s *priv) +{ + int ret; + + /* NOTE: The Ethernet clocks were initialized early in the boot-up + * sequence in stm32_rcc.c. + */ + + /* Initialize the free buffer list */ + + stm32_initbuffer(priv, &g_txbuffer[priv->intf * TXBUFFER_SIZE]); + + /* Reset the Ethernet block */ + + ninfo("Reset the Ethernet block\n"); + stm32_ethreset(priv); + + /* Initialize TX Descriptors list */ + + stm32_txdescinit(priv, + &g_txtable[priv->intf * CONFIG_STM32H7_ETH_NTXDESC]); + + /* Initialize RX Descriptors list */ + + stm32_rxdescinit(priv, + &g_rxtable[priv->intf * CONFIG_STM32H7_ETH_NRXDESC], + &g_rxbuffer[priv->intf * RXBUFFER_SIZE]); + + /* Initialize the PHY */ + + ninfo("Initialize the PHY\n"); + ret = stm32_phyinit(priv); + if (ret < 0) + { + return ret; + } + + /* Initialize the MAC and DMA */ + + ninfo("Initialize the MAC and DMA\n"); + ret = stm32_macconfig(priv); + if (ret < 0) + { + return ret; + } + + /* Enable normal MAC operation */ + + ninfo("Enable normal operation\n"); + return stm32_macenable(priv); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: stm32_ethinitialize + * + * Description: + * Initialize the Ethernet driver for one interface. If the STM32 chip + * supports multiple Ethernet controllers, then board specific logic + * must implement up_netinitialize() and call this function to initialize + * the desired interfaces. + * + * Parameters: + * intf - In the case where there are multiple EMACs, this value + * identifies which EMAC is to be initialized. + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +#if STM32H7_NETHERNET == 1 || defined(CONFIG_NETDEV_LATEINIT) +static inline +#endif + +int stm32_ethinitialize(int intf) +{ + struct stm32_ethmac_s *priv; + + ninfo("intf: %d\n", intf); + + /* Get the interface structure associated with this interface number. */ + + DEBUGASSERT(intf < STM32H7_NETHERNET); + priv = &g_stm32ethmac[intf]; + + /* Initialize the driver structure */ + + memset(priv, 0, sizeof(struct stm32_ethmac_s)); + priv->dev.d_ifup = stm32_ifup; /* I/F up (new IP address) callback */ + priv->dev.d_ifdown = stm32_ifdown; /* I/F down callback */ + priv->dev.d_txavail = stm32_txavail; /* New TX data callback */ +#ifdef CONFIG_NET_IGMP + priv->dev.d_addmac = stm32_addmac; /* Add multicast MAC address */ + priv->dev.d_rmmac = stm32_rmmac; /* Remove multicast MAC address */ +#endif +#ifdef CONFIG_NETDEV_PHY_IOCTL + priv->dev.d_ioctl = stm32_ioctl; /* Support PHY ioctl() calls */ +#endif + priv->dev.d_private = (void *)g_stm32ethmac; /* Used to recover private state from dev */ + priv->intf = intf; /* Remember the interface number */ + + /* Create a watchdog for timing polling for and timing of transmissions */ + + priv->txpoll = wd_create(); /* Create periodic poll timer */ + priv->txtimeout = wd_create(); /* Create TX timeout timer */ + + /* Configure GPIO pins to support Ethernet */ + + stm32_ethgpioconfig(priv); + + /* Attach the IRQ to the driver */ + + if (irq_attach(STM32_IRQ_ETH, stm32_interrupt, NULL)) + { + /* We could not attach the ISR to the interrupt */ + + return -EAGAIN; + } + + /* Put the interface in the down state. */ + + stm32_ifdown(&priv->dev); + + /* Register the device with the OS so that socket IOCTLs can be performed */ + + (void)netdev_register(&priv->dev, NET_LL_ETHERNET); + return OK; +} + +/**************************************************************************** + * Function: up_netinitialize + * + * Description: + * This is the "standard" network initialization logic called from the + * low-level initialization logic in up_initialize.c. If STM32H7_NETHERNET + * greater than one, then board specific logic will have to supply a + * version of up_netinitialize() that calls stm32_ethinitialize() with + * the appropriate interface number. + * + * Parameters: + * None. + * + * Returned Value: + * None. + * + * Assumptions: + * + ****************************************************************************/ + +#if STM32H7_NETHERNET == 1 && !defined(CONFIG_NETDEV_LATEINIT) +void up_netinitialize(void) +{ + (void)stm32_ethinitialize(0); +} +#endif + +#endif /* STM32H7_NETHERNET > 0 && CONFIG_STM32H7_ETHMAC */ diff --git a/arch/arm/src/stm32h7/stm32_ethernet.h b/arch/arm/src/stm32h7/stm32_ethernet.h new file mode 100644 index 00000000000..6e12eb84d74 --- /dev/null +++ b/arch/arm/src/stm32h7/stm32_ethernet.h @@ -0,0 +1,119 @@ +/**************************************************************************** + * arch/arm/src/stm32h7/stm32_ethernet.h + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H7_STM32_ETHERNET_H +#define __ARCH_ARM_SRC_STM32H7_STM32_ETHERNET_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip/stm32_ethernet.h" + +#if STM32H7_NETHERNET > 0 +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Function: stm32_ethinitialize + * + * Description: + * Initialize the Ethernet driver for one interface. If the STM32 chip + * supports multiple Ethernet controllers, then board specific logic must + * implement up_netinitialize() and call this function to initialize the + * desired interfaces. + * + * Parameters: + * intf - In the case where there are multiple EMACs, this value identifies + * which EMAC is to be initialized. + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +#if STM32H7_NETHERNET > 1 +int stm32_ethinitialize(int intf); +#endif + +/**************************************************************************** + * Function: stm32_phy_boardinitialize + * + * Description: + * Some boards require specialized initialization of the PHY before it can + * be used. This may include such things as configuring GPIOs, resetting + * the PHY, etc. If CONFIG_STM32H7_PHYINIT is defined in the configuration + * then the board specific logic must provide stm32_phyinitialize(); The + * STM32 Ethernet driver will call this function one time before it first + * uses the PHY. + * + * Parameters: + * intf - Always zero for now. + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ***************************************************************************/ + +#ifdef CONFIG_STM32H7_PHYINIT +int stm32_phy_boardinitialize(int intf); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* STM32H7_NETHERNET > 0 */ +#endif /* __ARCH_ARM_SRC_STM32H7_STM32_ETHERNET_H */ diff --git a/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c b/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c index 4c71fd31684..63bbcf3fe62 100644 --- a/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c +++ b/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c @@ -176,6 +176,13 @@ static inline void rcc_enableahb1(void) #endif #endif +#ifdef CONFIG_STM32H7_ETHMAC + /* Enable ethernet clocks */ + + regval |= (RCC_AHB1ENR_ETH1MACEN | RCC_AHB1ENR_ETH1TXEN | + RCC_AHB1ENR_ETH1RXEN); +#endif + putreg32(regval, STM32_RCC_AHB1ENR); /* Enable peripherals */ } diff --git a/configs/nucleo-h743zi/include/board.h b/configs/nucleo-h743zi/include/board.h index c8df224b17e..8de715cde29 100644 --- a/configs/nucleo-h743zi/include/board.h +++ b/configs/nucleo-h743zi/include/board.h @@ -263,6 +263,12 @@ #define STM32_SDMMC_CLKCR_EDGE STM32_SDMMC_CLKCR_NEGEDGE +/* Ethernet definitions ****************************************************************/ + +#define GPIO_ETH_RMII_TXD0 GPIO_ETH_RMII_TXD0_2 /* PG13 */ +#define GPIO_ETH_RMII_TXD1 GPIO_ETH_RMII_TXD1_1 /* PB 13 */ +#define GPIO_ETH_RMII_TX_EN GPIO_ETH_RMII_TX_EN_2 + /* LED definitions ******************************************************************/ /* The Nucleo-144 board has numerous LEDs but only three, LD1 a Green LED, LD2 a Blue * LED and LD3 a Red LED, that can be controlled by software. The following