From b561b4459b740a897cdad28963b018f82a43d98c Mon Sep 17 00:00:00 2001 From: stbenn Date: Fri, 8 Nov 2024 08:57:48 -0600 Subject: [PATCH] Add uid file needed for ethernet copied over files, not modified properly yet Squashing lots of define errors to get ethernet.c working quick path rename Fix STM32_EMAC_BASE (and ICACHE/DCACHE Bases). Enable ETH, ETHTX, and ETHRX with CONFIG_STM32H5_ETHMAC (like H7 does). Fixed stm32_get_uniqueid Previously, an exception occurred as a result of calling this function. The problem turned out to be this line: uniqueid[i] = *((uint8_t *)(STM32_SYSMEM_UID) + i);. Dereferencing a 32-bit register as a 8-bit register seemed to be causing this issue. I think this function is better anyway, fewer register accesses and loop iterations. Turn on SBS peripheral when using Ethernet The SBS peripheral is required to be turned on because the ETH_SEL_PHY bits need to be set. Without turning this on, RMII mode is not possible. MII may not work either. Style Updates --- arch/arm/include/stm32h5/chip.h | 6 + arch/arm/src/stm32h5/Make.defs | 8 +- arch/arm/src/stm32h5/chip.h | 8 + arch/arm/src/stm32h5/hardware/stm32_sbs.h | 6 +- arch/arm/src/stm32h5/stm32_ethernet.c | 4273 +++++++++++++++++ arch/arm/src/stm32h5/stm32_ethernet.h | 104 + arch/arm/src/stm32h5/stm32_uid.c | 66 + arch/arm/src/stm32h5/stm32_uid.h | 53 + arch/arm/src/stm32h5/stm32h5xx_rcc.c | 9 +- .../arm/stm32h5/nucleo-h563zi/include/board.h | 18 +- 10 files changed, 4528 insertions(+), 23 deletions(-) create mode 100644 arch/arm/src/stm32h5/stm32_ethernet.c create mode 100644 arch/arm/src/stm32h5/stm32_ethernet.h create mode 100644 arch/arm/src/stm32h5/stm32_uid.c create mode 100644 arch/arm/src/stm32h5/stm32_uid.h diff --git a/arch/arm/include/stm32h5/chip.h b/arch/arm/include/stm32h5/chip.h index 93e0ff1cbca..3db5fd6905a 100644 --- a/arch/arm/include/stm32h5/chip.h +++ b/arch/arm/include/stm32h5/chip.h @@ -103,4 +103,10 @@ #define NVIC_SYSH_PRIORITY_MAX 0x00 /* Zero is maximum priority */ #define NVIC_SYSH_PRIORITY_STEP 0x10 /* Four bits of interrupt priority used */ +#if defined(CONFIG_STM32H5_HAVE_ETHERNET) +# define STM32H5_NETHERNET 1 /* Ethernet MAC */ +#else +# define STM32H5_NETHERNET 0 /* No Ethernet MAC */ +#endif + #endif /* __ARCH_ARM_INCLUDE_STM32H5_CHIP_H */ diff --git a/arch/arm/src/stm32h5/Make.defs b/arch/arm/src/stm32h5/Make.defs index 96f055b5f99..d45240a0068 100644 --- a/arch/arm/src/stm32h5/Make.defs +++ b/arch/arm/src/stm32h5/Make.defs @@ -31,12 +31,10 @@ include armv8-m/Make.defs # Required STM32H5 files -CHIP_ASRCS = -CHIP_CSRCS = stm32_gpio.c -CHIP_CSRCS += stm32_irq.c stm32_lowputc.c stm32_rcc.c -CHIP_CSRCS += stm32_serial.c stm32_start.c -CHIP_CSRCS += stm32_pwr.c stm32_timerisr.c +CHIP_CSRCS += stm32_gpio.c stm32_irq.c stm32_lowputc.c stm32_rcc.c +CHIP_CSRCS += stm32_serial.c stm32_start.c stm32_pwr.c stm32_timerisr.c CHIP_CSRCS += stm32_lse.c stm32_lsi.c +CHIP_CSRCS += stm32_uid.c stm32_ethernet.c ifneq ($(CONFIG_ARCH_IDLE_CUSTOM),y) CHIP_CSRCS += stm32_idle.c diff --git a/arch/arm/src/stm32h5/chip.h b/arch/arm/src/stm32h5/chip.h index 2784ec1f0db..7cdcc950e3d 100644 --- a/arch/arm/src/stm32h5/chip.h +++ b/arch/arm/src/stm32h5/chip.h @@ -50,4 +50,12 @@ #define ARMV8M_PERIPHERAL_INTERRUPTS STM32_IRQ_NEXTINTS +/* Cache line sizes set in line with ST document RM0481 Rev 2. + * DCACHE implementation, pg 284, Table 93 + * ICACHE implementation, pg 367, Table 85. + */ + +#define ARMV8M_ICACHE_LINESIZE 16 /* 16 bytes */ +#define ARMV8M_DCACHE_LINESIZE 16 /* 16 bytes */ + #endif /* __ARCH_ARM_SRC_STM32H5_CHIP_H */ diff --git a/arch/arm/src/stm32h5/hardware/stm32_sbs.h b/arch/arm/src/stm32h5/hardware/stm32_sbs.h index 20938319102..7ea88c87705 100644 --- a/arch/arm/src/stm32h5/hardware/stm32_sbs.h +++ b/arch/arm/src/stm32h5/hardware/stm32_sbs.h @@ -93,8 +93,8 @@ #define SBS_PMCR_PB9_FMP (1 << 19) /* Fast-mode Plus on PB9*/ #define SBS_PMCR_ETH_SEL_PHY_SHIFT (21) /* Bits 23-21 Ethernet PHY interface selection */ -#define SBS_PMCR_ETH_SEL_PHY_MASK (0b111 << SBS_ETH_SEL_PHY_SHIFT) -# define SBS_PMCR_ETH_SEL_PHY_GMII_OR_MII (0 << SBS_ETH_SEL_PHY_SHIFT) -# define SBS_PMCR_ETH_SEL_PHY_RMII (4 << SBS_ETH_SEL_PHY_SHIFT) +#define SBS_PMCR_ETH_SEL_PHY_MASK (0b111 << SBS_PMCR_ETH_SEL_PHY_SHIFT) +# define SBS_PMCR_ETH_SEL_PHY_GMII_OR_MII (0 << SBS_PMCR_ETH_SEL_PHY_SHIFT) +# define SBS_PMCR_ETH_SEL_PHY_RMII (4 << SBS_PMCR_ETH_SEL_PHY_SHIFT) #endif /* __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32_SBS_H */ diff --git a/arch/arm/src/stm32h5/stm32_ethernet.c b/arch/arm/src/stm32h5/stm32_ethernet.c new file mode 100644 index 00000000000..43248623138 --- /dev/null +++ b/arch/arm/src/stm32h5/stm32_ethernet.c @@ -0,0 +1,4273 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/stm32_ethernet.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_NET_PKT) +# include +#endif + +#include +#include "arm_internal.h" +#include "barriers.h" + +#include "hardware/stm32_sbs.h" +#include "hardware/stm32_pinmap.h" +#include "stm32_gpio.h" +#include "stm32_rcc.h" +#include "stm32_ethernet.h" +#include "stm32_uid.h" + +#include + +/* STM32H5_NETHERNET determines the number of physical interfaces that can + * be supported by the hardware. CONFIG_STM32H5_ETHMAC will defined if + * any STM32H5 Ethernet support is enabled in the configuration. + */ + +#if STM32H5_NETHERNET > 0 && defined(CONFIG_STM32H5_ETHMAC) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Memory synchronization */ + +#define MEMORY_SYNC() do { ARM_DSB(); ARM_ISB(); } while (0) + +/* Configuration ************************************************************/ + +/* See boards/arm/stm32/stm3240g-eval/README.txt for an explanation of the + * configuration settings. + */ + +#if STM32H5_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_STM32H5_ETHMAC_HPWORK) +# define ETHWORK HPWORK +# elif defined(CONFIG_STM32H5_ETHMAC_LPWORK) +# define ETHWORK LPWORK +# else +# define ETHWORK LPWORK +# endif +#endif + +#ifndef CONFIG_STM32H5_PHYADDR +# error "CONFIG_STM32H5_PHYADDR must be defined in the NuttX configuration" +#endif + +#if !defined(CONFIG_STM32H5_MII) && !defined(CONFIG_STM32H5_RMII) +# warning "Neither CONFIG_STM32H5_MII nor CONFIG_STM32H5_RMII defined" +#endif + +#if defined(CONFIG_STM32H5_MII) && defined(CONFIG_STM32H5_RMII) +# error "Both CONFIG_STM32H5_MII and CONFIG_STM32H5_RMII defined" +#endif + +#ifdef CONFIG_STM32H5_MII +# if !defined(CONFIG_STM32H5_MII_MCO1) && !defined(CONFIG_STM32H5_MII_MCO2) && \ + !defined(CONFIG_STM32H5_MII_EXTCLK) +# warning "Neither CONFIG_STM32H5_MII_MCO1, CONFIG_STM32H5_MII_MCO2, nor CONFIG_STM32H5_MII_EXTCLK defined" +# endif +# if defined(CONFIG_STM32H5_MII_MCO1) && defined(CONFIG_STM32H5_MII_MCO2) +# error "Both CONFIG_STM32H5_MII_MCO1 and CONFIG_STM32H5_MII_MCO2 defined" +# endif +#endif + +#ifdef CONFIG_STM32H5_RMII +# if !defined(CONFIG_STM32H5_RMII_MCO1) && !defined(CONFIG_STM32H5_RMII_MCO2) && \ + !defined(CONFIG_STM32H5_RMII_EXTCLK) +# warning "Neither CONFIG_STM32H5_RMII_MCO1, CONFIG_STM32H5_RMII_MCO2, nor CONFIG_STM32H5_RMII_EXTCLK defined" +# endif +# if defined(CONFIG_STM32H5_RMII_MCO1) && defined(CONFIG_STM32H5_RMII_MCO2) +# error "Both CONFIG_STM32H5_RMII_MCO1 and CONFIG_STM32H5_RMII_MCO2 defined" +# endif +#endif + +#ifdef CONFIG_STM32H5_AUTONEG +# ifndef CONFIG_STM32H5_PHYSR +# error "CONFIG_STM32H5_PHYSR must be defined in the NuttX configuration" +# endif +# ifdef CONFIG_STM32H5_PHYSR_ALTCONFIG +# ifndef CONFIG_STM32H5_PHYSR_ALTMODE +# error "CONFIG_STM32H5_PHYSR_ALTMODE must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H5_PHYSR_10HD +# error "CONFIG_STM32H5_PHYSR_10HD must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H5_PHYSR_100HD +# error "CONFIG_STM32H5_PHYSR_100HD must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H5_PHYSR_10FD +# error "CONFIG_STM32H5_PHYSR_10FD must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H5_PHYSR_100FD +# error "CONFIG_STM32H5_PHYSR_100FD must be defined in the NuttX configuration" +# endif +# else +# ifndef CONFIG_STM32H5_PHYSR_SPEED +# error "CONFIG_STM32H5_PHYSR_SPEED must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H5_PHYSR_100MBPS +# error "CONFIG_STM32H5_PHYSR_100MBPS must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H5_PHYSR_MODE +# error "CONFIG_STM32H5_PHYSR_MODE must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_STM32H5_PHYSR_FULLDUPLEX +# error "CONFIG_STM32H5_PHYSR_FULLDUPLEX must be defined in the NuttX configuration" +# endif +# endif +#endif + +#ifdef CONFIG_STM32H5_ETH_PTP +# warning "CONFIG_STM32H5_ETH_PTP is not yet supported" +#endif + +#undef CONFIG_STM32H5_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_STM32H5_ETH_BUFSIZE +# define ETH_BUFSIZE CONFIG_STM32H5_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 are using an incomplete/untested configuration" +#endif + +#ifndef CONFIG_STM32H5_ETH_NRXDESC +# define CONFIG_STM32H5_ETH_NRXDESC 8 +#endif +#ifndef CONFIG_STM32H5_ETH_NTXDESC +# define CONFIG_STM32H5_ETH_NTXDESC 4 +#endif + +/* We need at least one more free buffer than transmit buffers */ + +#define STM32_ETH_NFREEBUFFERS (CONFIG_STM32H5_ETH_NTXDESC+1) + +/* Buffers used for 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 (ARMV8M_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 (STM32H5_NETHERNET * CONFIG_STM32H5_ETH_NRXDESC) +#define TXTABLE_SIZE (STM32H5_NETHERNET * CONFIG_STM32H5_ETH_NTXDESC) + +#define RXBUFFER_SIZE (CONFIG_STM32H5_ETH_NRXDESC * ALIGNED_BUFSIZE) +#define RXBUFFER_ALLOC (STM32H5_NETHERNET * RXBUFFER_SIZE) + +#define TXBUFFER_SIZE (STM32_ETH_NFREEBUFFERS * ALIGNED_BUFSIZE) +#define TXBUFFER_ALLOC (STM32H5_NETHERNET * TXBUFFER_SIZE) + +/* Extremely detailed register debug that you would normally never want + * enabled. + */ + +#ifndef CONFIG_DEBUG_NET_INFO +# undef CONFIG_STM32H5_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 <= 250000000 +# define ETH_MACMDIOAR_CR ETH_MACMDIOAR_CR_DIV102 +#elif STM32_HCLK_FREQUENCY >= 250000000 && STM32_HCLK_FREQUENCY <= 300000000 +# define ETH_MACMDIOAR_CR ETH_MACMDIOAR_CR_DIV124 +#else +# error "STM32_HCLK_FREQUENCY not supportable" +#endif + +/* Timing *******************************************************************/ + +/* 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 (0x0001998) + +/* MAC reset ready delays in loop counts */ + +#define MAC_READY_USTIMEOUT (200) + +/* 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_STM32H5_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_STM32H5_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 MACPFR 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_PAM 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_PAM | 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_HUC Hash unicast 0 (perfect + * dest filtering) + * ETH_MACPFR_HMC Hash multicast 0 (perfect + * dest filtering) + * ETH_MACPFR_DAIF Destination address inverse filtering 0 (normal) + * ETH_MACPFR_PAM Pass all multicast 0 (Depends on HMC + * bit) + * ETH_MACPFR_DBF 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 0 (disabled) + */ + +#ifdef CONFIG_NET_PROMISCUOUS +# define MACPFR_SET_BITS (ETH_MACPFR_PCF_PAUSE | ETH_MACPFR_PM) +#else +# define MACPFR_SET_BITS (ETH_MACPFR_PCF_PAUSE) +#endif + +/* 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_STM32H5_ETH_HWCHECKSUM +/* TODO */ + +# error CONFIG_STM32H5_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) + +#define ETH_DMAINT_ERROR_ENABLE (ETH_DMACIER_AIE | ETH_DMAINT_ABNORMAL) + +/* 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 */ + struct wdog_s txtimeout; /* TX timeout timer */ + struct work_s irqwork; /* For deferring interrupt work to the work queue */ + struct work_s pollwork; /* For deferring poll work to the work queue */ + + /* This holds the information visible to the NuttX network */ + + struct net_driver_s dev; /* Interface understood by the network */ + + /* 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] +aligned_data(ARMV8M_DCACHE_LINESIZE); +static union stm32_desc_u g_txtable[TXTABLE_SIZE] +aligned_data(ARMV8M_DCACHE_LINESIZE); + +/* Buffer allocations */ + +static uint8_t g_rxbuffer[RXBUFFER_ALLOC] +aligned_data(ARMV8M_DCACHE_LINESIZE); +static uint8_t g_txbuffer[TXBUFFER_ALLOC] +aligned_data(ARMV8M_DCACHE_LINESIZE); + +/* These are the pre-allocated Ethernet device structures */ + +static struct stm32_ethmac_s g_stm32ethmac[STM32H5_NETHERNET]; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register operations ******************************************************/ + +#ifdef CONFIG_STM32H5_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, void *arg); + +/* Watchdog timer expirations */ + +static void stm32_txtimeout_work(void *arg); +static void stm32_txtimeout_expiry(wdparm_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 */ +#ifndef CONFIG_STM32H5_NO_PHY +#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_STM32H5_ETHMAC_REGDEBUG +static void stm32_phyregdump(void); +#endif +#endif + +/* MAC/DMA Initialization */ + +#ifdef CONFIG_STM32H5_MII +static inline void stm32_selectmii(void); +#endif +#ifdef CONFIG_STM32H5_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); +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_STM32H5_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_STM32H5_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_STM32H5_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 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_STM32H5_ETH_NTXDESC]; + union stm32_desc_u *last = + &g_txtable[priv->intf * CONFIG_STM32H5_ETH_NTXDESC + + CONFIG_STM32H5_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: %08" PRIx32 "\n", + priv->dev.d_len, priv->dev.d_buf, txdesc, txdesc->des3); + + DEBUGASSERT(txdesc); + + /* 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++) + { + 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 segment 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; + 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 + { + DEBUGASSERT((txdesc->des3 & ETH_TDES3_RD_OWN) == 0); + + /* 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_STM32H5_ETH_NTXDESC) + { + stm32_disableint(priv, ETH_DMACIER_RIE); + } + + MEMORY_SYNC(); + + /* Enable TX interrupts */ + + stm32_enableint(priv, ETH_DMACIER_TIE); + + /* Setup the TX timeout watchdog (perhaps restarting the timer) */ + + wd_start(&priv->txtimeout, STM32_TXTIMEOUT, + stm32_txtimeout_expiry, (wdparm_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); + + /* 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_STM32H5_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. + */ + + nerr("No tx descriptors available"); + + 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. */ + + nerr("No tx buffer available"); + + 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_STM32H5_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) + { + 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; + } + } + else + { + nerr("No tx buffers"); + } + } + else + { + nerr("No tx descriptors\n"); + } +} + +/**************************************************************************** + * 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_STM32H5_ETH_NRXDESC]; + union stm32_desc_u *last = + &g_rxtable[priv->intf * CONFIG_STM32H5_ETH_NRXDESC + + CONFIG_STM32H5_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)); + + /* Get the next RX descriptor in the chain */ + + rxdesc = stm32_get_next_rxdesc(priv, rxdesc); + + /* Update the tail pointer */ + + stm32_putreg((uintptr_t)rxdesc, STM32_ETH_DMACRXDTPR); + } + + /* 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) + { + /* Clear the RBU flag */ + + stm32_putreg(ETH_DMACSR_RBU, STM32_ETH_DMACSR); + + 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 = NULL; + 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_STM32H5_ETH_NRXDESC && + priv->inflight < CONFIG_STM32H5_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; + + if (priv->segments > 1 || + dev->d_len > ALIGNED_BUFSIZE) + { + /* The Frame is to big, it spans segments */ + + nerr("ERROR: Dropped, RX descriptor Too big: %d in %d " + "segments\n", dev->d_len, priv->segments); + + stm32_freesegment(priv, rxcurr, priv->segments); + } + else + { + /* 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 + + MIN(dev->d_len, ALIGNED_BUFSIZE)); + + 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: " + "%08" PRIx32 "\n", + rxdesc->des3); + stm32_freesegment(priv, rxcurr, priv->segments); + } + } + } + else + { + /* Drop the context descriptors, we are not interested */ + + DEBUGASSERT(rxcurr != NULL); + 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); + + /* Free dropped packet buffer */ + + if (dev->d_buf) + { + stm32_freebuffer(priv, dev->d_buf); + dev->d_buf = NULL; + dev->d_len = 0; + } + + 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"); + + /* Receive an IPv4 packet from the network device */ + + ipv4_input(&priv->dev); + + /* If the above function invocation resulted in data that should + * be sent out on the network, the field d_len will set to a + * value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* And send the packet */ + + stm32_transmit(priv); + } + } + else +#endif +#ifdef CONFIG_NET_IPv6 + if (BUF->type == HTONS(ETHTYPE_IP6)) + { + ninfo("IPv6 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) + { + /* 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_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) + { + 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; + + 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)); + + while ((txdesc->des3 & ETH_TDES3_RD_OWN) == 0) + { + /* There should be a buffer assigned to all in-flight + * TX descriptors. + */ + + ninfo("txtail: %p des0: %08" PRIx32 + " des2: %08" PRIx32 " des3: %08" PRIx32 "\n", + txdesc, txdesc->des0, txdesc->des2, txdesc->des3); + + DEBUGASSERT(txdesc->des0 != 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 interrupts may 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); + } + + /* Check if there are pending "abnormal" interrupts */ + + if ((dmasr & ETH_DMACSR_AIS) != 0) + { + /* Just let the user know what happened */ + + nerr("ERROR: Abnormal event(s): %08" PRIx32 "\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); + + /* In case of any error that stops the DMA, reset the MAC. */ + + if (dmasr & (ETH_DMACIER_CDEE | ETH_DMACSR_FBE | + ETH_DMACSR_RPS | ETH_DMACSR_TPS)) + { + /* As per the datasheet's recommendation, the MAC + * needs to be reset for all fatal errors. The + * scheduled job will take the interface down and + * up again. + */ + + work_queue(ETHWORK, &priv->irqwork, stm32_txtimeout_work, priv, 0); + + /* Interrupts need to remain disabled, no other + * processing will take place. After reset + * everything will be restored. + */ + + net_unlock(); + return; + } + } + + 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, 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: + * arg - The argument + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * + ****************************************************************************/ + +static void stm32_txtimeout_expiry(wdparm_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_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: %u.%u.%u.%u\n", + ip4_addr1(dev->d_ipaddr), ip4_addr2(dev->d_ipaddr), + ip4_addr3(dev->d_ipaddr), ip4_addr4(dev->d_ipaddr)); +#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; + } + + /* 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 timeout timers */ + + 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_MACHT1R; + hashindex -= 32; + } + else + { + registeraddress = STM32_ETH_MACHT0R; + } + + temp = stm32_getreg(registeraddress); + temp |= 1 << hashindex; + stm32_putreg(temp, registeraddress); + + temp = stm32_getreg(STM32_ETH_MACPFR); + temp |= (ETH_MACPFR_HMC | 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_HMC | 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_STM32H5_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_STM32H5_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_STM32H5_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_STM32H5_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_STM32H5_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) +{ +#ifndef CONFIG_STM32H5_NO_PHY +#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_STM32H5_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; +#else + return -EIO; +#endif +} +#endif /* CONFIG_NETDEV_PHY_IOCTL */ + +#ifndef CONFIG_STM32H5_NO_PHY +/**************************************************************************** + * 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[3: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[3: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 MACMDIODR 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_STM32H5_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_STM32H5_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_STM32H5_ETHMAC_REGDEBUG +static void stm32_phyregdump() +{ + uint16_t phyval; + int ret; + int i; + + for (i = 0; i < 0x20; i++) + { + ret = stm32_phyread(CONFIG_STM32H5_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_STM32H5_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 CR field in the MACMDIOAR reg */ + + 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_STM32H5_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_STM32H5_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_STM32H5_ETHMAC_REGDEBUG + stm32_phyregdump(); +#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_STM32H5_AUTONEG + /* Wait for link status */ + + for (timeout = 0; timeout < PHY_RETRY_TIMEOUT; timeout++) + { + ret = stm32_phyread(CONFIG_STM32H5_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; + } + + nxsig_usleep(100); + } + + 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_STM32H5_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_STM32H5_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; + } + + nxsig_usleep(100); + } + + 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_STM32H5_PHYADDR, CONFIG_STM32H5_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_STM32H5_PHYSR, phyval); + + /* Different PHYs present speed and mode information in different ways. IF + * This CONFIG_STM32H5_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_STM32H5_PHYSR_ALTCONFIG + switch (phyval & CONFIG_STM32H5_PHYSR_ALTMODE) + { + default: + nerr("ERROR: Unrecognized PHY status setting\n"); + + /* Falls through */ + + case CONFIG_STM32H5_PHYSR_10HD: + priv->fduplex = 0; + priv->mbps100 = 0; + break; + + case CONFIG_STM32H5_PHYSR_100HD: + priv->fduplex = 0; + priv->mbps100 = 1; + break; + + case CONFIG_STM32H5_PHYSR_10FD: + priv->fduplex = 1; + priv->mbps100 = 0; + break; + + case CONFIG_STM32H5_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_STM32H5_PHYSR_MODE) == + CONFIG_STM32H5_PHYSR_FULLDUPLEX) + { + priv->fduplex = 1; + } + + if ((phyval & CONFIG_STM32H5_PHYSR_SPEED) == CONFIG_STM32H5_PHYSR_100MBPS) + { + priv->mbps100 = 1; + } +#endif + +#else /* Auto-negotiation not selected */ + + phyval = 0; +#ifdef CONFIG_STM32H5_ETHFD + phyval |= MII_MCR_FULLDPLX; +#endif +#ifdef CONFIG_STM32H5_ETH100MBPS + phyval |= MII_MCR_SPEED100; +#endif + + ret = stm32_phywrite(CONFIG_STM32H5_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_STM32H5_ETHFD + priv->fduplex = 1; +#endif +#ifdef CONFIG_STM32H5_ETH100MBPS + priv->mbps100 = 1; +#endif +#endif + + ninfo("Duplex: %s Speed: %d MBps\n", + priv->fduplex ? "FULL" : "HALF", + priv->mbps100 ? 100 : 10); + + return OK; +} + +#endif + +/**************************************************************************** + * Name: stm32_selectmii + * + * Description: + * Selects the MII interface. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H5_MII +#pragma message "If TrustZone is enabled, MII will fail to select." +static inline void stm32_selectmii(void) +{ + uint32_t regval; + + regval = getreg32(STM32_SBS_PMCR); + regval &= ~SBS_PMCR_ETH_SEL_PHY_MASK; + regval |= SBS_PMCR_ETH_SEL_PHY_GMII_OR_MII; + putreg32(regval, STM32_SBS_PMCR); +} +#endif + +/**************************************************************************** + * Name: stm32_selectrmii + * + * Description: + * Selects the RMII interface. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H5_RMII +#pragma message "If TrustZone is enabled, RMII will fail to select." +static inline void stm32_selectrmii(void) +{ + uint32_t regval; + + regval = getreg32(STM32_SBS_PMCR); + regval &= ~SBS_PMCR_ETH_SEL_PHY_MASK; + regval |= SBS_PMCR_ETH_SEL_PHY_RMII; + putreg32(regval, STM32_SBS_PMCR); +} +#endif + +/**************************************************************************** + * 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_STM32H5_MII) || defined(CONFIG_STM32H5_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_STM32H5_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_STM32H5_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_STM32H5_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_STM32H5_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_STM32H5_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_STM32H5_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_STM32H5_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_STM32H5_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_STM32H5_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; + volatile uint32_t timeout; + + /* Reset the Ethernet on the AHB1 bus */ + + regval = stm32_getreg(STM32_RCC_AHB1RSTR); + regval |= RCC_AHB1RSTR_ETHRST; + stm32_putreg(regval, STM32_RCC_AHB1RSTR); + + regval &= ~RCC_AHB1RSTR_ETHRST; + 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. + */ + + timeout = MAC_READY_USTIMEOUT; + while (timeout-- && (stm32_getreg(STM32_ETH_DMAMR) & ETH_DMAMR_SWR) != 0) + { + up_udelay(1); + } + + /* According 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_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); + + /* 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_DMACSR); + regval |= (ETH_DMACSR_TPS | ETH_DMACSR_RPS); + stm32_putreg(regval, STM32_ETH_DMACSR); + + 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. + */ + +#ifdef CONFIG_STM32H5_PHYINIT + /* Perform any necessary, board-specific PHY initialization */ + + ret = stm32_phy_boardinitialize(0); + if (ret < 0) + { + nerr("ERROR: Failed to initialize the PHY: %d\n", ret); + return ret; + } +#endif + + /* 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_STM32H5_ETH_NTXDESC]); + + /* Initialize RX Descriptors list */ + + stm32_rxdescinit(priv, + &g_rxtable[priv->intf * CONFIG_STM32H5_ETH_NRXDESC], + &g_rxbuffer[priv->intf * RXBUFFER_SIZE]); + + /* Initialize the PHY */ + +#ifdef CONFIG_STM32H5_NO_PHY + ninfo("MAC without PHY\n"); +#ifdef CONFIG_STM32H5_ETHFD + priv->fduplex = 1; +#else + priv->fduplex = 0; +#endif +#ifdef CONFIG_STM32H5_ETH100MBPS + priv->mbps100 = 1; +#else + priv->mbps100 = 0; +#endif +#else + ninfo("Initialize the PHY\n"); + ret = stm32_phyinit(priv); + if (ret < 0) + { + return ret; + } + +#endif + /* 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 arm_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 STM32H5_NETHERNET > 1 || defined(CONFIG_NETDEV_LATEINIT) +int stm32_ethinitialize(int intf) +#else +static inline int stm32_ethinitialize(int intf) +#endif +{ + struct stm32_ethmac_s *priv; + uint8_t uid[12]; + uint64_t crc; + int ret = OK; + + ninfo("intf: %d\n", intf); + + /* Get the interface structure associated with this interface number. */ + + DEBUGASSERT(intf < STM32H5_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 = g_stm32ethmac; /* Used to recover private state */ + priv->intf = intf; /* Remember the interface number */ + + stm32_get_uniqueid(uid); + crc = crc64(uid, 12); + + /* Specify as localy administrated address */ + + priv->dev.d_mac.ether.ether_addr_octet[0] = (crc >> 0) | 0x02; + priv->dev.d_mac.ether.ether_addr_octet[0] &= ~0x1; + + priv->dev.d_mac.ether.ether_addr_octet[1] = crc >> 8; + priv->dev.d_mac.ether.ether_addr_octet[2] = crc >> 16; + priv->dev.d_mac.ether.ether_addr_octet[3] = crc >> 24; + priv->dev.d_mac.ether.ether_addr_octet[4] = crc >> 32; + priv->dev.d_mac.ether.ether_addr_octet[5] = crc >> 40; + + /* 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; + } + +#ifdef CONFIG_STM32H5_PHYINIT + /* Perform any necessary, board-specific PHY initialization */ + + ret = stm32_phy_boardinitialize(0); + if (ret < 0) + { + nerr("ERROR: Failed to initialize the PHY: %d\n", ret); + return ret; + } +#endif + + /* Put the interface in the down state. */ + + stm32_ifdown(&priv->dev); + + /* Register the device with the OS so that socket IOCTLs can be performed */ + + netdev_register(&priv->dev, NET_LL_ETHERNET); + return ret; +} + +/**************************************************************************** + * Function: arm_netinitialize + * + * Description: + * This is the "standard" network initialization logic called from the + * low-level initialization logic in arm_initialize.c. If STM32H5_NETHERNET + * greater than one, then board specific logic will have to supply a + * version of arm_netinitialize() that calls stm32_ethinitialize() with + * the appropriate interface number. + * + * Parameters: + * None. + * + * Returned Value: + * None. + * + * Assumptions: + * + ****************************************************************************/ + +#if STM32H5_NETHERNET == 1 && !defined(CONFIG_NETDEV_LATEINIT) +void arm_netinitialize(void) +{ + stm32_ethinitialize(0); +} +#endif + +#endif /* STM32H5_NETHERNET > 0 && CONFIG_STM32H5_ETHMAC */ diff --git a/arch/arm/src/stm32h5/stm32_ethernet.h b/arch/arm/src/stm32h5/stm32_ethernet.h new file mode 100644 index 00000000000..66107aa0181 --- /dev/null +++ b/arch/arm/src/stm32h5/stm32_ethernet.h @@ -0,0 +1,104 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/stm32_ethernet.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H5_STM32_ETHERNET_H +#define __ARCH_ARM_SRC_STM32H5_STM32_ETHERNET_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "hardware/stm32_ethernet.h" + +#if STM32H5_NETHERNET > 0 +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#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 arm_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 STM32H5_NETHERNET > 1 || defined(CONFIG_NETDEV_LATEINIT) +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_STM32H5_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_STM32H5_PHYINIT +int stm32_phy_boardinitialize(int intf); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* STM32H5_NETHERNET > 0 */ +#endif /* __ARCH_ARM_SRC_STM32H5_STM32_ETHERNET_H */ \ No newline at end of file diff --git a/arch/arm/src/stm32h5/stm32_uid.c b/arch/arm/src/stm32h5/stm32_uid.c new file mode 100644 index 00000000000..bda001d2760 --- /dev/null +++ b/arch/arm/src/stm32h5/stm32_uid.c @@ -0,0 +1,66 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/stm32_uid.c + * + * Copyright (C) 2015 Marawan Ragab. All rights reserved. + * Authors: Marawan Ragab + * David Sidrane + * Modified for STM32H5 by Tyler Bennett + * + * 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 "hardware/stm32_memorymap.h" + +#include "stm32_uid.h" + +#ifdef STM32_SYSMEM_UID + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void stm32_get_uniqueid(uint8_t uniqueid[12]) +{ + int i; + uint32_t *uid = (uint32_t *) uniqueid; + + for (i = 0; i < 3; i++) + { + *uid = *((uint32_t *)(STM32_SYSMEM_UID) + i); + uid++; + } +} + +#endif /* STM32_SYSMEM_UID */ diff --git a/arch/arm/src/stm32h5/stm32_uid.h b/arch/arm/src/stm32h5/stm32_uid.h new file mode 100644 index 00000000000..d63e07e6c90 --- /dev/null +++ b/arch/arm/src/stm32h5/stm32_uid.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/stm32_uid.h + * + * Copyright (C) 2015 Marawan Ragab. All rights reserved. + * Authors: Marawan Ragab + * David Sidrane + * Modified for STM32H5 by Tyler Bennett + * + * 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_STM32H5_STM32_UID_H +#define __ARCH_ARM_SRC_STM32H5_STM32_UID_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void stm32_get_uniqueid(uint8_t uniqueid[12]); + +#endif /* __ARCH_ARM_SRC_STM32H5_STM32_UID_H */ \ No newline at end of file diff --git a/arch/arm/src/stm32h5/stm32h5xx_rcc.c b/arch/arm/src/stm32h5/stm32h5xx_rcc.c index b01905edb3c..b99fba47dec 100644 --- a/arch/arm/src/stm32h5/stm32h5xx_rcc.c +++ b/arch/arm/src/stm32h5/stm32h5xx_rcc.c @@ -130,18 +130,15 @@ static inline void rcc_enableahb1(void) regval |= RCC_AHB1ENR_RAMCFGEN; #endif -#ifdef CONFIG_STM32H5_ETH +#ifdef CONFIG_STM32H5_ETHMAC /* ETH clock enable */ regval |= RCC_AHB1ENR_ETHEN; -#endif -#ifdef CONFIG_STM32H5_ETHTX + /* ETH TX clock enable */ regval |= RCC_AHB1ENR_ETHTXEN; -#endif -#ifdef CONFIG_STM32H5_ETHRX /* ETH RX clock enable */ regval |= RCC_AHB1ENR_ETHRXEN; @@ -597,7 +594,7 @@ static inline void rcc_enableapb3(void) regval = getreg32(STM32_RCC_APB3ENR); -#ifdef CONFIG_STM32H5_SBS +#if defined(CONFIG_STM32H5_SBS) || defined(CONFIG_STM32H5_ETHMAC) /* Bit 1: SBS clock enable */ regval |= RCC_APB3ENR_SBSEN; diff --git a/boards/arm/stm32h5/nucleo-h563zi/include/board.h b/boards/arm/stm32h5/nucleo-h563zi/include/board.h index e077dec0eb4..1b9079ff86f 100644 --- a/boards/arm/stm32h5/nucleo-h563zi/include/board.h +++ b/boards/arm/stm32h5/nucleo-h563zi/include/board.h @@ -231,15 +231,15 @@ /* Ethernet definitions *****************************************************/ -#define GPIO_ETH_MDC (GPIO_ETH_MDC_0 | GPIO_SPEED_100MHz) /* PC1 */ -#define GPIO_ETH_MDIO (GPIO_ETH_MDIO_0 | GPIO_SPEED_100MHz) /* PA2 */ -#define GPIO_ETH_RMII_RXD0 (GPIO_ETH_RMII_RXD0_0 | GPIO_SPEED_100MHz) /* PC4 */ -#define GPIO_ETH_RMII_RXD1 (GPIO_ETH_RMII_RXD1_0 | GPIO_SPEED_100MHz) /* PC5 */ -#define GPIO_ETH_RMII_TXD0 (GPIO_ETH_RMII_TXD0_3 | GPIO_SPEED_100MHz) /* PG13 */ -#define GPIO_ETH_RMII_TXD1 (GPIO_ETH_RMII_TXD1_1 | GPIO_SPEED_100MHz) /* PB15 */ -#define GPIO_ETH_RMII_TX_EN (GPIO_ETH_RMII_TX_EN_3 | GPIO_SPEED_100MHz) /* PG11 */ -#define GPIO_ETH_RMII_CRS_DV (GPIO_ETH_RMII_CRS_DV_0 | GPIO_SPEED_100MHz) /* PA7 */ -#define GPIO_ETH_RMII_REF_CLK (GPIO_ETH_RMII_REF_CLK_0 | GPIO_SPEED_100MHz) /* PA1 */ +#define GPIO_ETH_MDC (GPIO_ETH_MDC_0 | GPIO_SPEED_100MHZ) /* PC1 */ +#define GPIO_ETH_MDIO (GPIO_ETH_MDIO_0 | GPIO_SPEED_100MHZ) /* PA2 */ +#define GPIO_ETH_RMII_RXD0 (GPIO_ETH_RMII_RXD0_0 | GPIO_SPEED_100MHZ) /* PC4 */ +#define GPIO_ETH_RMII_RXD1 (GPIO_ETH_RMII_RXD1_0 | GPIO_SPEED_100MHZ) /* PC5 */ +#define GPIO_ETH_RMII_TXD0 (GPIO_ETH_RMII_TXD0_3 | GPIO_SPEED_100MHZ) /* PG13 */ +#define GPIO_ETH_RMII_TXD1 (GPIO_ETH_RMII_TXD1_1 | GPIO_SPEED_100MHZ) /* PB15 */ +#define GPIO_ETH_RMII_TX_EN (GPIO_ETH_RMII_TX_EN_3 | GPIO_SPEED_100MHZ) /* PG11 */ +#define GPIO_ETH_RMII_CRS_DV (GPIO_ETH_RMII_CRS_DV_0 | GPIO_SPEED_100MHZ) /* PA7 */ +#define GPIO_ETH_RMII_REF_CLK (GPIO_ETH_RMII_REF_CLK_0 | GPIO_SPEED_100MHZ) /* PA1 */ /* ADC Clock Source *********************************************************/