diff --git a/boards/stm32h563zi_nucleo/Makefile.inc b/boards/stm32h563zi_nucleo/Makefile.inc index 3a18ddc..f37f470 100644 --- a/boards/stm32h563zi_nucleo/Makefile.inc +++ b/boards/stm32h563zi_nucleo/Makefile.inc @@ -1,7 +1,7 @@ _BOARD_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) PLATFORM = stm32h5 -TESTS ?= clock gpio timer flash rng +TESTS ?= clock gpio timer flash rng eth GCC = $(GCC_PATH)arm-none-eabi-gcc LD = $(GCC_PATH)arm-none-eabi-ld @@ -29,6 +29,8 @@ BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/spi.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/rng.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/crypto.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/block.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/eth.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/eth_phy/*.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/stm32h5_*.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/systick.c) diff --git a/boards/stm32h563zi_nucleo/board.c b/boards/stm32h563zi_nucleo/board.c index 5171fdf..926961b 100644 --- a/boards/stm32h563zi_nucleo/board.c +++ b/boards/stm32h563zi_nucleo/board.c @@ -4,6 +4,7 @@ #include #include "board.h" #include +#include #include "peripheral.h" /* SysTick timing */ @@ -52,13 +53,22 @@ static const whal_Stm32h5Rcc_Clk g_clocks[] = { {WHAL_STM32H563_GPIOA_CLOCK}, {WHAL_STM32H563_GPIOB_CLOCK}, {WHAL_STM32H563_GPIOD_CLOCK}, + {WHAL_STM32H563_GPIOC_CLOCK}, {WHAL_STM32H563_GPIOG_CLOCK}, {WHAL_STM32H563_USART2_CLOCK}, {WHAL_STM32H563_SPI1_CLOCK}, {WHAL_STM32H563_RNG_CLOCK}, + {WHAL_STM32H563_SBS_CLOCK}, }; #define CLOCK_COUNT (sizeof(g_clocks) / sizeof(g_clocks[0])) +static const whal_Stm32h5Rcc_Clk g_ethClocks[] = { + {WHAL_STM32H563_ETH_CLOCK}, + {WHAL_STM32H563_ETHTX_CLOCK}, + {WHAL_STM32H563_ETHRX_CLOCK}, +}; +#define ETH_CLOCK_COUNT (sizeof(g_ethClocks) / sizeof(g_ethClocks[0])) + /* GPIO */ whal_Gpio g_whalGpio = { WHAL_STM32H563_GPIO_DEVICE, @@ -127,6 +137,78 @@ whal_Gpio g_whalGpio = { .speed = WHAL_STM32H5_GPIO_SPEED_FAST, .pull = WHAL_STM32H5_GPIO_PULL_UP, }, + [ETH_RMII_REF_CLK_PIN] = { /* RMII REF_CLK on PA1 */ + .port = WHAL_STM32H5_GPIO_PORT_A, + .pin = 1, + .mode = WHAL_STM32H5_GPIO_MODE_ALTFN, + .speed = WHAL_STM32H5_GPIO_SPEED_HIGH, + .pull = WHAL_STM32H5_GPIO_PULL_NONE, + .altFn = 11, + }, + [ETH_RMII_MDIO_PIN] = { /* RMII MDIO on PA2 */ + .port = WHAL_STM32H5_GPIO_PORT_A, + .pin = 2, + .mode = WHAL_STM32H5_GPIO_MODE_ALTFN, + .speed = WHAL_STM32H5_GPIO_SPEED_HIGH, + .pull = WHAL_STM32H5_GPIO_PULL_NONE, + .altFn = 11, + }, + [ETH_RMII_MDC_PIN] = { /* RMII MDC on PC1 */ + .port = WHAL_STM32H5_GPIO_PORT_C, + .pin = 1, + .mode = WHAL_STM32H5_GPIO_MODE_ALTFN, + .speed = WHAL_STM32H5_GPIO_SPEED_HIGH, + .pull = WHAL_STM32H5_GPIO_PULL_NONE, + .altFn = 11, + }, + [ETH_RMII_CRS_DV_PIN] = { /* RMII CRS_DV on PA7 */ + .port = WHAL_STM32H5_GPIO_PORT_A, + .pin = 7, + .mode = WHAL_STM32H5_GPIO_MODE_ALTFN, + .speed = WHAL_STM32H5_GPIO_SPEED_HIGH, + .pull = WHAL_STM32H5_GPIO_PULL_NONE, + .altFn = 11, + }, + [ETH_RMII_RXD0_PIN] = { /* RMII RXD0 on PC4 */ + .port = WHAL_STM32H5_GPIO_PORT_C, + .pin = 4, + .mode = WHAL_STM32H5_GPIO_MODE_ALTFN, + .speed = WHAL_STM32H5_GPIO_SPEED_HIGH, + .pull = WHAL_STM32H5_GPIO_PULL_NONE, + .altFn = 11, + }, + [ETH_RMII_RXD1_PIN] = { /* RMII RXD1 on PC5 */ + .port = WHAL_STM32H5_GPIO_PORT_C, + .pin = 5, + .mode = WHAL_STM32H5_GPIO_MODE_ALTFN, + .speed = WHAL_STM32H5_GPIO_SPEED_HIGH, + .pull = WHAL_STM32H5_GPIO_PULL_NONE, + .altFn = 11, + }, + [ETH_RMII_TX_EN_PIN] = { /* RMII TX_EN on PG11 */ + .port = WHAL_STM32H5_GPIO_PORT_G, + .pin = 11, + .mode = WHAL_STM32H5_GPIO_MODE_ALTFN, + .speed = WHAL_STM32H5_GPIO_SPEED_HIGH, + .pull = WHAL_STM32H5_GPIO_PULL_NONE, + .altFn = 11, + }, + [ETH_RMII_TXD0_PIN] = { /* RMII TXD0 on PG13 */ + .port = WHAL_STM32H5_GPIO_PORT_G, + .pin = 13, + .mode = WHAL_STM32H5_GPIO_MODE_ALTFN, + .speed = WHAL_STM32H5_GPIO_SPEED_HIGH, + .pull = WHAL_STM32H5_GPIO_PULL_NONE, + .altFn = 11, + }, + [ETH_RMII_TXD1_PIN] = { /* RMII TXD1 on PB15 */ + .port = WHAL_STM32H5_GPIO_PORT_B, + .pin = 15, + .mode = WHAL_STM32H5_GPIO_MODE_ALTFN, + .speed = WHAL_STM32H5_GPIO_SPEED_HIGH, + .pull = WHAL_STM32H5_GPIO_PULL_NONE, + .altFn = 11, + }, }, .pinCount = PIN_COUNT, }, @@ -184,6 +266,49 @@ whal_Flash g_whalFlash = { }, }; +/* Ethernet */ +#define ETH_TX_DESC_COUNT 4 +#define ETH_RX_DESC_COUNT 4 +#define ETH_TX_BUF_SIZE 1536 +#define ETH_RX_BUF_SIZE 1536 + +static whal_Stm32h5Eth_TxDesc ethTxDescs[ETH_TX_DESC_COUNT] + __attribute__((aligned(16))); +static whal_Stm32h5Eth_RxDesc ethRxDescs[ETH_RX_DESC_COUNT] + __attribute__((aligned(16))); +static uint8_t ethTxBufs[ETH_TX_DESC_COUNT * ETH_TX_BUF_SIZE] + __attribute__((aligned(4))); +static uint8_t ethRxBufs[ETH_RX_DESC_COUNT * ETH_RX_BUF_SIZE] + __attribute__((aligned(4))); + +whal_Eth g_whalEth = { + WHAL_STM32H563_ETH_DEVICE, + + .cfg = &(whal_Stm32h5Eth_Cfg) { + .macAddr = {0x00, 0x80, 0xE1, 0x00, 0x00, 0x01}, + .txDescs = ethTxDescs, + .txBufs = ethTxBufs, + .txDescCount = ETH_TX_DESC_COUNT, + .txBufSize = ETH_TX_BUF_SIZE, + .rxDescs = ethRxDescs, + .rxBufs = ethRxBufs, + .rxDescCount = ETH_RX_DESC_COUNT, + .rxBufSize = ETH_RX_BUF_SIZE, + .timeout = &g_whalTimeout, + }, +}; + +/* Ethernet PHY (LAN8742A) */ +whal_EthPhy g_whalEthPhy = { + .eth = &g_whalEth, + .addr = BOARD_ETH_PHY_ADDR, + .driver = &whal_Lan8742a_Driver, + + .cfg = &(whal_Lan8742a_Cfg) { + .timeout = &g_whalTimeout, + }, +}; + void Board_WaitMs(size_t ms) { uint32_t startCount = g_tick; @@ -210,13 +335,24 @@ whal_Error Board_Init(void) if (err) return err; - /* Enable clocks */ + /* Enable clocks (excludes ETH — needs SBS RMII config first) */ for (size_t i = 0; i < CLOCK_COUNT; i++) { err = whal_Clock_Enable(&g_whalClock, &g_clocks[i]); if (err) return err; } + /* Select RMII mode in SBS_PMCR (SBS base 0x44000400 + 0x100) bits [23:21] = 0b100 */ + *(volatile uint32_t *)0x44000500 = + (*(volatile uint32_t *)0x44000500 & ~(7UL << 21)) | (4UL << 21); + + /* Enable ETH clocks after RMII mode is selected */ + for (size_t i = 0; i < ETH_CLOCK_COUNT; i++) { + err = whal_Clock_Enable(&g_whalClock, &g_ethClocks[i]); + if (err) + return err; + } + /* Enable HSI48 for RNG kernel clock */ err = whal_Stm32h5Rcc_Ext_EnableHsi48(&g_whalClock, 1); if (err) @@ -238,6 +374,14 @@ whal_Error Board_Init(void) if (err) return err; + err = whal_Eth_Init(&g_whalEth); + if (err) + return err; + + err = whal_EthPhy_Init(&g_whalEthPhy); + if (err) + return err; + err = whal_Timer_Init(&g_whalTimer); if (err) return err; @@ -269,6 +413,14 @@ whal_Error Board_Deinit(void) if (err) return err; + err = whal_EthPhy_Deinit(&g_whalEthPhy); + if (err) + return err; + + err = whal_Eth_Deinit(&g_whalEth); + if (err) + return err; + err = whal_Rng_Deinit(&g_whalRng); if (err) return err; @@ -285,6 +437,13 @@ whal_Error Board_Deinit(void) if (err) return err; + /* Disable ETH clocks */ + for (size_t i = 0; i < ETH_CLOCK_COUNT; i++) { + err = whal_Clock_Disable(&g_whalClock, &g_ethClocks[i]); + if (err) + return err; + } + /* Disable clocks */ for (size_t i = 0; i < CLOCK_COUNT; i++) { err = whal_Clock_Disable(&g_whalClock, &g_clocks[i]); diff --git a/boards/stm32h563zi_nucleo/board.h b/boards/stm32h563zi_nucleo/board.h index 013fd16..a0e2649 100644 --- a/boards/stm32h563zi_nucleo/board.h +++ b/boards/stm32h563zi_nucleo/board.h @@ -12,6 +12,8 @@ extern whal_Uart g_whalUart; extern whal_Spi g_whalSpi; extern whal_Rng g_whalRng; extern whal_Flash g_whalFlash; +extern whal_Eth g_whalEth; +extern whal_EthPhy g_whalEthPhy; extern whal_Timeout g_whalTimeout; extern volatile uint32_t g_tick; @@ -24,6 +26,15 @@ enum { SPI_MISO_PIN, SPI_MOSI_PIN, SPI_CS_PIN, + ETH_RMII_REF_CLK_PIN, + ETH_RMII_MDIO_PIN, + ETH_RMII_MDC_PIN, + ETH_RMII_CRS_DV_PIN, + ETH_RMII_RXD0_PIN, + ETH_RMII_RXD1_PIN, + ETH_RMII_TX_EN_PIN, + ETH_RMII_TXD0_PIN, + ETH_RMII_TXD1_PIN, PIN_COUNT, }; @@ -33,6 +44,11 @@ enum { #define BOARD_FLASH_TEST_ADDR 0x081FE000 #define BOARD_FLASH_SECTOR_SZ 0x2000 +/* Ethernet PHY: LAN8742A on MDIO address 0 */ +#define BOARD_ETH_PHY_ADDR 0 +#define BOARD_ETH_PHY_ID1 0x0007 +#define BOARD_ETH_PHY_ID2 0xC131 + whal_Error Board_Init(void); whal_Error Board_Deinit(void); void Board_WaitMs(size_t ms); diff --git a/docs/writing_a_driver.md b/docs/writing_a_driver.md index 7f1ff3f..03cc257 100644 --- a/docs/writing_a_driver.md +++ b/docs/writing_a_driver.md @@ -792,3 +792,95 @@ calling Clock Init. ### Disable Disable a specific power supply output. The inverse of Enable. + +--- + +## Ethernet + +Header: `wolfHAL/eth/eth.h` + +The Ethernet driver controls a MAC (Media Access Controller) with an integrated +DMA engine. It manages descriptor rings in RAM for transmit and receive, handles +MDIO bus access for PHY communication, and configures the MAC for the negotiated +link speed and duplex. + +### Init + +Initialize the MAC, DMA, and MTL. Set up TX and RX descriptor rings in RAM, +program the MAC address, and configure DMA bus mode and burst lengths. Does NOT +enable TX/RX — call Start for that. The descriptor and buffer memory must be +pre-allocated by the board and passed via the config struct. Validate all config +fields (descriptor counts > 0, buffer pointers non-NULL, buffer sizes > 0). + +### Deinit + +Perform a DMA software reset to clear all state. + +### Start + +Configure the MAC speed and duplex to match the PHY, enable MAC TX/RX, start +the DMA TX and RX engines, and kick the RX DMA by writing the tail pointer. +Speed and duplex are passed as parameters — the board reads these from the PHY +driver before calling Start. + +### Stop + +Stop DMA TX and RX engines, then disable MAC TX and RX. + +### Send + +Transmit a single Ethernet frame. Find the next available TX descriptor (OWN=0), +copy the frame into the pre-allocated TX buffer, fill in the descriptor fields +(buffer address, length, OWN=1, FD, LD), and write the DMA tail pointer to kick +transmission. Return WHAL_ENOTREADY if no descriptor is available. Validate that +the frame length does not exceed the TX buffer size. + +### Recv + +Receive a single Ethernet frame by polling. Check the next RX descriptor — if +OWN=0, DMA has written a frame. Read the packet length from the descriptor, copy +the frame data to the caller's buffer, re-arm the descriptor (set OWN=1), and +update the tail pointer. Return WHAL_ENOTREADY if no frame is available, or +WHAL_EHARDWARE if the error summary bit is set. + +### MdioRead + +Read a 16-bit PHY register via the MDIO management bus. Write the PHY address, +register address, and read command to the MDIO address register, poll for +completion, then read the data register. Use a timeout to avoid infinite hang +if the PHY is not responding. + +### MdioWrite + +Write a 16-bit value to a PHY register via MDIO. Write the data first, then +issue the write command and poll for completion with a timeout. + +--- + +## EthPhy + +Header: `wolfHAL/eth_phy/eth_phy.h` + +The Ethernet PHY driver handles link negotiation and status for an external PHY +chip connected to a MAC via the MDIO bus. The PHY device struct holds a pointer +to its parent MAC (for MDIO access) and the PHY address on the bus. Different +PHY chips (e.g., LAN8742A, DP83848) have different vendor-specific registers +but share the same API. + +### Init + +Reset the PHY via software reset (BCR bit 15), wait for the reset bit to +self-clear, then enable autonegotiation. Does not block waiting for link — the +board or application polls GetLinkState separately. + +### Deinit + +Power down the PHY or release resources. May be a no-op on simple PHYs. + +### GetLinkState + +Read the current link status, negotiated speed, and duplex mode. The IEEE 802.3 +BSR register (reg 1) link bit is latching-low — read it twice and use the second +result for current status. Speed and duplex are read from a vendor-specific +status register (e.g., register 0x1F on LAN8742A). Return speed as 10 or 100, +and duplex as WHAL_ETH_DUPLEX_HALF or WHAL_ETH_DUPLEX_FULL. diff --git a/src/eth/eth.c b/src/eth/eth.c new file mode 100644 index 0000000..070a296 --- /dev/null +++ b/src/eth/eth.c @@ -0,0 +1,62 @@ +#include +#include + +whal_Error whal_Eth_Init(whal_Eth *ethDev) +{ + if (!ethDev || !ethDev->driver || !ethDev->driver->Init) + return WHAL_EINVAL; + return ethDev->driver->Init(ethDev); +} + +whal_Error whal_Eth_Deinit(whal_Eth *ethDev) +{ + if (!ethDev || !ethDev->driver || !ethDev->driver->Deinit) + return WHAL_EINVAL; + return ethDev->driver->Deinit(ethDev); +} + +whal_Error whal_Eth_Start(whal_Eth *ethDev, uint8_t speed, + uint8_t duplex) +{ + if (!ethDev || !ethDev->driver || !ethDev->driver->Start) + return WHAL_EINVAL; + return ethDev->driver->Start(ethDev, speed, duplex); +} + +whal_Error whal_Eth_Stop(whal_Eth *ethDev) +{ + if (!ethDev || !ethDev->driver || !ethDev->driver->Stop) + return WHAL_EINVAL; + return ethDev->driver->Stop(ethDev); +} + +whal_Error whal_Eth_Send(whal_Eth *ethDev, const uint8_t *frame, + size_t len) +{ + if (!ethDev || !ethDev->driver || !ethDev->driver->Send || !frame) + return WHAL_EINVAL; + return ethDev->driver->Send(ethDev, frame, len); +} + +whal_Error whal_Eth_Recv(whal_Eth *ethDev, uint8_t *frame, size_t *len) +{ + if (!ethDev || !ethDev->driver || !ethDev->driver->Recv || !frame || !len) + return WHAL_EINVAL; + return ethDev->driver->Recv(ethDev, frame, len); +} + +whal_Error whal_Eth_MdioRead(whal_Eth *ethDev, uint8_t phyAddr, + uint8_t reg, uint16_t *val) +{ + if (!ethDev || !ethDev->driver || !ethDev->driver->MdioRead || !val) + return WHAL_EINVAL; + return ethDev->driver->MdioRead(ethDev, phyAddr, reg, val); +} + +whal_Error whal_Eth_MdioWrite(whal_Eth *ethDev, uint8_t phyAddr, + uint8_t reg, uint16_t val) +{ + if (!ethDev || !ethDev->driver || !ethDev->driver->MdioWrite) + return WHAL_EINVAL; + return ethDev->driver->MdioWrite(ethDev, phyAddr, reg, val); +} diff --git a/src/eth/stm32h5_eth.c b/src/eth/stm32h5_eth.c new file mode 100644 index 0000000..3c8c07e --- /dev/null +++ b/src/eth/stm32h5_eth.c @@ -0,0 +1,500 @@ +#include +#include +#include +#include +#include +#include + +/* + * STM32H5 Ethernet MAC Register Definitions + * + * The peripheral has three register blocks at offsets from the ETH base: + * MAC: 0x0000 MTL: 0x0C00 DMA: 0x1000 + */ + +/* --- MAC registers --- */ +#define ETH_MACCR_REG 0x0000 +#define ETH_MACCR_RE_Pos 0 +#define ETH_MACCR_RE_Msk (1UL << ETH_MACCR_RE_Pos) +#define ETH_MACCR_TE_Pos 1 +#define ETH_MACCR_TE_Msk (1UL << ETH_MACCR_TE_Pos) +#define ETH_MACCR_DM_Pos 13 +#define ETH_MACCR_DM_Msk (1UL << ETH_MACCR_DM_Pos) +#define ETH_MACCR_FES_Pos 14 +#define ETH_MACCR_FES_Msk (1UL << ETH_MACCR_FES_Pos) + +#define ETH_MACPFR_REG 0x0008 + +#define ETH_MACA0HR_REG 0x0300 +#define ETH_MACA0LR_REG 0x0304 + +#define ETH_MACMDIOAR_REG 0x0200 +#define ETH_MACMDIOAR_MB_Pos 0 +#define ETH_MACMDIOAR_MB_Msk (1UL << ETH_MACMDIOAR_MB_Pos) +#define ETH_MACMDIOAR_GOC_Pos 2 +#define ETH_MACMDIOAR_GOC_Msk (3UL << ETH_MACMDIOAR_GOC_Pos) +#define ETH_MACMDIOAR_CR_Pos 8 +#define ETH_MACMDIOAR_CR_Msk (0xFUL << ETH_MACMDIOAR_CR_Pos) +#define ETH_MACMDIOAR_RDA_Pos 16 +#define ETH_MACMDIOAR_RDA_Msk (0x1FUL << ETH_MACMDIOAR_RDA_Pos) +#define ETH_MACMDIOAR_PA_Pos 21 +#define ETH_MACMDIOAR_PA_Msk (0x1FUL << ETH_MACMDIOAR_PA_Pos) + +#define ETH_MACMDIODR_REG 0x0204 +#define ETH_MACMDIODR_MD_Msk 0xFFFFUL + +#define ETH_MDIO_GOC_WRITE 0x1 +#define ETH_MDIO_GOC_READ 0x3 + +/* --- MTL registers --- */ +#define ETH_MTLTXQOMR_REG 0x0D00 +#define ETH_MTLTXQOMR_TSF_Pos 1 +#define ETH_MTLTXQOMR_TSF_Msk (1UL << ETH_MTLTXQOMR_TSF_Pos) +#define ETH_MTLTXQOMR_TXQEN_Pos 2 +#define ETH_MTLTXQOMR_TXQEN_Msk (3UL << ETH_MTLTXQOMR_TXQEN_Pos) + +#define ETH_MTLRXQOMR_REG 0x0D30 +#define ETH_MTLRXQOMR_FUP_Pos 3 +#define ETH_MTLRXQOMR_FUP_Msk (1UL << ETH_MTLRXQOMR_FUP_Pos) +#define ETH_MTLRXQOMR_FEP_Pos 4 +#define ETH_MTLRXQOMR_FEP_Msk (1UL << ETH_MTLRXQOMR_FEP_Pos) +#define ETH_MTLRXQOMR_RSF_Pos 5 +#define ETH_MTLRXQOMR_RSF_Msk (1UL << ETH_MTLRXQOMR_RSF_Pos) + +/* --- DMA registers --- */ +#define ETH_DMAMR_REG 0x1000 +#define ETH_DMAMR_SWR_Pos 0 +#define ETH_DMAMR_SWR_Msk (1UL << ETH_DMAMR_SWR_Pos) + +#define ETH_DMASBMR_REG 0x1004 +#define ETH_DMASBMR_FB_Pos 0 +#define ETH_DMASBMR_FB_Msk (1UL << ETH_DMASBMR_FB_Pos) +#define ETH_DMASBMR_AAL_Pos 12 +#define ETH_DMASBMR_AAL_Msk (1UL << ETH_DMASBMR_AAL_Pos) + +#define ETH_DMACCR_REG 0x1100 + +#define ETH_DMACTXCR_REG 0x1104 +#define ETH_DMACTXCR_ST_Pos 0 +#define ETH_DMACTXCR_ST_Msk (1UL << ETH_DMACTXCR_ST_Pos) +#define ETH_DMACTXCR_TXPBL_Pos 16 +#define ETH_DMACTXCR_TXPBL_Msk (0x3FUL << ETH_DMACTXCR_TXPBL_Pos) + +#define ETH_DMACRXCR_REG 0x1108 +#define ETH_DMACRXCR_SR_Pos 0 +#define ETH_DMACRXCR_SR_Msk (1UL << ETH_DMACRXCR_SR_Pos) +#define ETH_DMACRXCR_RBSZ_Pos 1 +#define ETH_DMACRXCR_RBSZ_Msk (0x3FFFUL << ETH_DMACRXCR_RBSZ_Pos) +#define ETH_DMACRXCR_RXPBL_Pos 16 +#define ETH_DMACRXCR_RXPBL_Msk (0x3FUL << ETH_DMACRXCR_RXPBL_Pos) + +#define ETH_DMACTXDLAR_REG 0x1114 +#define ETH_DMACRXDLAR_REG 0x111C +#define ETH_DMACTXDTPR_REG 0x1120 +#define ETH_DMACRXDTPR_REG 0x1128 +#define ETH_DMACTXRLR_REG 0x112C +#define ETH_DMACRXRLR_REG 0x1130 + +#define ETH_DMACSR_REG 0x1160 +#define ETH_DMACSR_TI_Msk (1UL << 0) +#define ETH_DMACSR_RI_Msk (1UL << 6) + +/* --- TX descriptor bits (TDES3) --- */ +#define TDES3_OWN (1UL << 31) +#define TDES3_FD (1UL << 29) +#define TDES3_LD (1UL << 28) + +/* TX descriptor bits (TDES2) */ +#define TDES2_IOC (1UL << 31) + +/* --- RX descriptor bits (RDES3) --- */ +#define RDES3_OWN (1UL << 31) +#define RDES3_IOC (1UL << 30) +#define RDES3_BUF1V (1UL << 24) +#define RDES3_ES (1UL << 15) +#define RDES3_PL_Msk 0x7FFFUL + +/* Default burst length */ +#define ETH_PBL 32 + +/* Max TX frame size */ +#define ETH_MAX_FRAME_SIZE 1536 + +/* MDIO clock range for 168 MHz AHB: CR=4 gives /102 ≈ 1.6 MHz */ +#define ETH_MDIO_CR 4 + +static whal_Error MdioPoll(size_t base, whal_Timeout *timeout) +{ + return whal_Reg_ReadPoll(base, ETH_MACMDIOAR_REG, + ETH_MACMDIOAR_MB_Msk, 0, timeout); +} + +whal_Error whal_Stm32h5Eth_Init(whal_Eth *ethDev) +{ + whal_Stm32h5Eth_Cfg *cfg; + size_t base; + whal_Error err; + + if (!ethDev || !ethDev->cfg) + return WHAL_EINVAL; + + cfg = (whal_Stm32h5Eth_Cfg *)ethDev->cfg; + base = ethDev->regmap.base; + + if (!cfg->txDescs || !cfg->txBufs || cfg->txDescCount == 0 || + !cfg->rxDescs || !cfg->rxBufs || cfg->rxDescCount == 0 || + cfg->txBufSize == 0 || cfg->rxBufSize == 0) + return WHAL_EINVAL; + + /* DMA software reset */ + whal_Reg_Update(base, ETH_DMAMR_REG, ETH_DMAMR_SWR_Msk, + ETH_DMAMR_SWR_Msk); + err = whal_Reg_ReadPoll(base, ETH_DMAMR_REG, ETH_DMAMR_SWR_Msk, 0, + cfg->timeout); + if (err) + return err; + + /* DMA bus mode: fixed burst, address-aligned */ + whal_Reg_Write(base, ETH_DMASBMR_REG, + ETH_DMASBMR_FB_Msk | ETH_DMASBMR_AAL_Msk); + + /* DMA channel: contiguous descriptors (DSL=0) */ + whal_Reg_Write(base, ETH_DMACCR_REG, 0); + + /* DMA TX: burst length, not started yet */ + whal_Reg_Write(base, ETH_DMACTXCR_REG, + whal_SetBits(ETH_DMACTXCR_TXPBL_Msk, + ETH_DMACTXCR_TXPBL_Pos, ETH_PBL)); + + /* DMA RX: burst length, buffer size, not started yet */ + whal_Reg_Write(base, ETH_DMACRXCR_REG, + whal_SetBits(ETH_DMACRXCR_RXPBL_Msk, + ETH_DMACRXCR_RXPBL_Pos, ETH_PBL) | + whal_SetBits(ETH_DMACRXCR_RBSZ_Msk, + ETH_DMACRXCR_RBSZ_Pos, cfg->rxBufSize)); + + /* Set up TX descriptor ring */ + for (size_t i = 0; i < cfg->txDescCount; i++) { + cfg->txDescs[i].des[0] = 0; + cfg->txDescs[i].des[1] = 0; + cfg->txDescs[i].des[2] = 0; + cfg->txDescs[i].des[3] = 0; + } + + /* Set up RX descriptor ring with pre-allocated buffers */ + for (size_t i = 0; i < cfg->rxDescCount; i++) { + cfg->rxDescs[i].des[0] = (uintptr_t)(cfg->rxBufs + i * cfg->rxBufSize); + cfg->rxDescs[i].des[1] = 0; + cfg->rxDescs[i].des[2] = 0; + cfg->rxDescs[i].des[3] = RDES3_OWN | RDES3_IOC | RDES3_BUF1V; + } + + /* Program descriptor ring addresses and lengths */ + whal_Reg_Write(base, ETH_DMACTXDLAR_REG, (uintptr_t)cfg->txDescs); + whal_Reg_Write(base, ETH_DMACRXDLAR_REG, (uintptr_t)cfg->rxDescs); + whal_Reg_Write(base, ETH_DMACTXRLR_REG, cfg->txDescCount - 1); + whal_Reg_Write(base, ETH_DMACRXRLR_REG, cfg->rxDescCount - 1); + + /* MTL: store-and-forward for TX and RX, enable TX queue */ + whal_Reg_Update(base, ETH_MTLTXQOMR_REG, + ETH_MTLTXQOMR_TSF_Msk | ETH_MTLTXQOMR_TXQEN_Msk, + ETH_MTLTXQOMR_TSF_Msk | + whal_SetBits(ETH_MTLTXQOMR_TXQEN_Msk, + ETH_MTLTXQOMR_TXQEN_Pos, 2)); + whal_Reg_Update(base, ETH_MTLRXQOMR_REG, + ETH_MTLRXQOMR_RSF_Msk, ETH_MTLRXQOMR_RSF_Msk); + + /* MAC address */ + whal_Reg_Write(base, ETH_MACA0LR_REG, + ((uint32_t)cfg->macAddr[3] << 24) | + ((uint32_t)cfg->macAddr[2] << 16) | + ((uint32_t)cfg->macAddr[1] << 8) | + ((uint32_t)cfg->macAddr[0])); + whal_Reg_Write(base, ETH_MACA0HR_REG, + ((uint32_t)cfg->macAddr[5] << 8) | + ((uint32_t)cfg->macAddr[4])); + + /* Reset ring tracking state */ + cfg->txHead = 0; + cfg->rxHead = 0; + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32h5Eth_Deinit(whal_Eth *ethDev) +{ + if (!ethDev) + return WHAL_EINVAL; + + whal_Reg_Update(ethDev->regmap.base, ETH_DMAMR_REG, + ETH_DMAMR_SWR_Msk, ETH_DMAMR_SWR_Msk); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32h5Eth_Start(whal_Eth *ethDev, uint8_t speed, + uint8_t duplex) +{ + whal_Stm32h5Eth_Cfg *cfg; + size_t base; + + if (!ethDev || !ethDev->cfg) + return WHAL_EINVAL; + + cfg = (whal_Stm32h5Eth_Cfg *)ethDev->cfg; + base = ethDev->regmap.base; + + /* Configure MAC speed and duplex to match PHY */ + whal_Reg_Update(base, ETH_MACCR_REG, + ETH_MACCR_FES_Msk | ETH_MACCR_DM_Msk, + ((speed == 100) ? ETH_MACCR_FES_Msk : 0) | + (duplex ? ETH_MACCR_DM_Msk : 0)); + + /* Enable MAC TX and RX */ + whal_Reg_Update(base, ETH_MACCR_REG, + ETH_MACCR_TE_Msk | ETH_MACCR_RE_Msk, + ETH_MACCR_TE_Msk | ETH_MACCR_RE_Msk); + + /* Start DMA TX */ + whal_Reg_Update(base, ETH_DMACTXCR_REG, + ETH_DMACTXCR_ST_Msk, ETH_DMACTXCR_ST_Msk); + + /* Start DMA RX */ + whal_Reg_Update(base, ETH_DMACRXCR_REG, + ETH_DMACRXCR_SR_Msk, ETH_DMACRXCR_SR_Msk); + + /* Kick RX DMA by writing tail pointer past last descriptor */ + whal_Reg_Write(base, ETH_DMACRXDTPR_REG, + (uintptr_t)&cfg->rxDescs[cfg->rxDescCount]); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32h5Eth_Stop(whal_Eth *ethDev) +{ + size_t base; + + if (!ethDev) + return WHAL_EINVAL; + + base = ethDev->regmap.base; + + /* Stop DMA TX */ + whal_Reg_Update(base, ETH_DMACTXCR_REG, ETH_DMACTXCR_ST_Msk, 0); + + /* Stop DMA RX */ + whal_Reg_Update(base, ETH_DMACRXCR_REG, ETH_DMACRXCR_SR_Msk, 0); + + /* Disable MAC TX and RX */ + whal_Reg_Update(base, ETH_MACCR_REG, + ETH_MACCR_TE_Msk | ETH_MACCR_RE_Msk, 0); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32h5Eth_Send(whal_Eth *ethDev, const uint8_t *frame, + size_t len) +{ + whal_Stm32h5Eth_Cfg *cfg; + whal_Stm32h5Eth_TxDesc *desc; + size_t base; + size_t idx; + + if (!ethDev || !ethDev->cfg || !frame || len == 0) + return WHAL_EINVAL; + + cfg = (whal_Stm32h5Eth_Cfg *)ethDev->cfg; + + if (len > cfg->txBufSize) + return WHAL_EINVAL; + base = ethDev->regmap.base; + idx = cfg->txHead; + desc = &cfg->txDescs[idx]; + + /* Check if descriptor is available (OWN must be 0) */ + if (desc->des[3] & TDES3_OWN) + return WHAL_ENOTREADY; + + /* Copy frame into TX buffer */ + uint8_t *txBuf = cfg->txBufs + idx * cfg->txBufSize; + for (size_t i = 0; i < len; i++) + txBuf[i] = frame[i]; + + /* Set up descriptor */ + desc->des[0] = (uintptr_t)txBuf; + desc->des[1] = 0; + desc->des[2] = (len & 0x3FFF) | TDES2_IOC; + desc->des[3] = TDES3_OWN | TDES3_FD | TDES3_LD | (len & 0x7FFF); + + /* Advance ring position */ + cfg->txHead = (idx + 1) % cfg->txDescCount; + + /* Kick DMA — tail pointer past the end of the ring */ + whal_Reg_Write(base, ETH_DMACTXDTPR_REG, + (uintptr_t)&cfg->txDescs[cfg->txDescCount]); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32h5Eth_Recv(whal_Eth *ethDev, uint8_t *frame, + size_t *len) +{ + whal_Stm32h5Eth_Cfg *cfg; + whal_Stm32h5Eth_RxDesc *desc; + size_t base; + size_t idx; + uint32_t rdes3; + size_t pktLen; + + if (!ethDev || !ethDev->cfg || !frame || !len) + return WHAL_EINVAL; + + cfg = (whal_Stm32h5Eth_Cfg *)ethDev->cfg; + base = ethDev->regmap.base; + idx = cfg->rxHead; + desc = &cfg->rxDescs[idx]; + + rdes3 = desc->des[3]; + + /* Check if DMA has released this descriptor */ + if (rdes3 & RDES3_OWN) + return WHAL_ENOTREADY; + + /* Check for errors */ + if (rdes3 & RDES3_ES) { + desc->des[0] = (uintptr_t)(cfg->rxBufs + idx * cfg->rxBufSize); + desc->des[3] = RDES3_OWN | RDES3_IOC | RDES3_BUF1V; + cfg->rxHead = (idx + 1) % cfg->rxDescCount; + whal_Reg_Write(base, ETH_DMACRXDTPR_REG, + (uintptr_t)&cfg->rxDescs[cfg->rxDescCount]); + return WHAL_EHARDWARE; + } + + /* Extract packet length (includes CRC) */ + pktLen = rdes3 & RDES3_PL_Msk; + if (pktLen > *len) + pktLen = *len; + + /* Copy frame data */ + uint8_t *rxBuf = (uint8_t *)(cfg->rxBufs + idx * cfg->rxBufSize); + for (size_t i = 0; i < pktLen; i++) + frame[i] = rxBuf[i]; + *len = pktLen; + + /* Re-arm descriptor for DMA */ + desc->des[0] = (uintptr_t)rxBuf; + desc->des[1] = 0; + desc->des[2] = 0; + desc->des[3] = RDES3_OWN | RDES3_IOC | RDES3_BUF1V; + + /* Advance ring position */ + cfg->rxHead = (idx + 1) % cfg->rxDescCount; + + /* Update RX tail pointer — always past end of ring */ + whal_Reg_Write(base, ETH_DMACRXDTPR_REG, + (uintptr_t)&cfg->rxDescs[cfg->rxDescCount]); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32h5Eth_MdioRead(whal_Eth *ethDev, uint8_t phyAddr, + uint8_t reg, uint16_t *val) +{ + whal_Stm32h5Eth_Cfg *cfg; + size_t base; + whal_Error err; + + if (!ethDev || !ethDev->cfg || !val) + return WHAL_EINVAL; + + cfg = (whal_Stm32h5Eth_Cfg *)ethDev->cfg; + base = ethDev->regmap.base; + + err = MdioPoll(base, cfg->timeout); + if (err) + return err; + + whal_Reg_Write(base, ETH_MACMDIOAR_REG, + whal_SetBits(ETH_MACMDIOAR_PA_Msk, ETH_MACMDIOAR_PA_Pos, + phyAddr) | + whal_SetBits(ETH_MACMDIOAR_RDA_Msk, ETH_MACMDIOAR_RDA_Pos, + reg) | + whal_SetBits(ETH_MACMDIOAR_CR_Msk, ETH_MACMDIOAR_CR_Pos, + ETH_MDIO_CR) | + whal_SetBits(ETH_MACMDIOAR_GOC_Msk, ETH_MACMDIOAR_GOC_Pos, + ETH_MDIO_GOC_READ) | + ETH_MACMDIOAR_MB_Msk); + + err = MdioPoll(base, cfg->timeout); + if (err) + return err; + + *val = (uint16_t)(whal_Reg_Read(base, ETH_MACMDIODR_REG) & + ETH_MACMDIODR_MD_Msk); + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32h5Eth_MdioWrite(whal_Eth *ethDev, uint8_t phyAddr, + uint8_t reg, uint16_t val) +{ + whal_Stm32h5Eth_Cfg *cfg; + size_t base; + whal_Error err; + + if (!ethDev || !ethDev->cfg) + return WHAL_EINVAL; + + cfg = (whal_Stm32h5Eth_Cfg *)ethDev->cfg; + base = ethDev->regmap.base; + + err = MdioPoll(base, cfg->timeout); + if (err) + return err; + + whal_Reg_Write(base, ETH_MACMDIODR_REG, val); + + whal_Reg_Write(base, ETH_MACMDIOAR_REG, + whal_SetBits(ETH_MACMDIOAR_PA_Msk, ETH_MACMDIOAR_PA_Pos, + phyAddr) | + whal_SetBits(ETH_MACMDIOAR_RDA_Msk, ETH_MACMDIOAR_RDA_Pos, + reg) | + whal_SetBits(ETH_MACMDIOAR_CR_Msk, ETH_MACMDIOAR_CR_Pos, + ETH_MDIO_CR) | + whal_SetBits(ETH_MACMDIOAR_GOC_Msk, ETH_MACMDIOAR_GOC_Pos, + ETH_MDIO_GOC_WRITE) | + ETH_MACMDIOAR_MB_Msk); + + err = MdioPoll(base, cfg->timeout); + if (err) + return err; + + return WHAL_SUCCESS; +} + +#define ETH_MACCR_LM_Pos 12 +#define ETH_MACCR_LM_Msk (1UL << ETH_MACCR_LM_Pos) + +whal_Error whal_Stm32h5Eth_Ext_EnableLoopback(whal_Eth *ethDev, + uint8_t enable) +{ + if (!ethDev) + return WHAL_EINVAL; + + whal_Reg_Update(ethDev->regmap.base, ETH_MACCR_REG, ETH_MACCR_LM_Msk, + whal_SetBits(ETH_MACCR_LM_Msk, ETH_MACCR_LM_Pos, + enable ? 1 : 0)); + + return WHAL_SUCCESS; +} + +const whal_EthDriver whal_Stm32h5Eth_Driver = { + .Init = whal_Stm32h5Eth_Init, + .Deinit = whal_Stm32h5Eth_Deinit, + .Start = whal_Stm32h5Eth_Start, + .Stop = whal_Stm32h5Eth_Stop, + .Send = whal_Stm32h5Eth_Send, + .Recv = whal_Stm32h5Eth_Recv, + .MdioRead = whal_Stm32h5Eth_MdioRead, + .MdioWrite = whal_Stm32h5Eth_MdioWrite, +}; diff --git a/src/eth_phy/eth_phy.c b/src/eth_phy/eth_phy.c new file mode 100644 index 0000000..bfd687b --- /dev/null +++ b/src/eth_phy/eth_phy.c @@ -0,0 +1,25 @@ +#include +#include + +whal_Error whal_EthPhy_Init(whal_EthPhy *phyDev) +{ + if (!phyDev || !phyDev->driver || !phyDev->driver->Init) + return WHAL_EINVAL; + return phyDev->driver->Init(phyDev); +} + +whal_Error whal_EthPhy_Deinit(whal_EthPhy *phyDev) +{ + if (!phyDev || !phyDev->driver || !phyDev->driver->Deinit) + return WHAL_EINVAL; + return phyDev->driver->Deinit(phyDev); +} + +whal_Error whal_EthPhy_GetLinkState(whal_EthPhy *phyDev, uint8_t *up, + uint8_t *speed, uint8_t *duplex) +{ + if (!phyDev || !phyDev->driver || !phyDev->driver->GetLinkState || + !up || !speed || !duplex) + return WHAL_EINVAL; + return phyDev->driver->GetLinkState(phyDev, up, speed, duplex); +} diff --git a/src/eth_phy/lan8742a.c b/src/eth_phy/lan8742a.c new file mode 100644 index 0000000..4f63d6b --- /dev/null +++ b/src/eth_phy/lan8742a.c @@ -0,0 +1,128 @@ +#include +#include +#include + +/* + * LAN8742A PHY Register Definitions (IEEE 802.3 standard + vendor) + */ + +#define PHY_BCR 0x00 /* Basic Control Register */ +#define PHY_BCR_RESET (1UL << 15) +#define PHY_BCR_ANEN (1UL << 12) + +#define PHY_BSR 0x01 /* Basic Status Register */ +#define PHY_BSR_LINK (1UL << 2) +#define PHY_BSR_ANEG_COMPLETE (1UL << 5) + +#define PHY_PHYSCSR 0x1F /* PHY Special Control/Status Register */ +#define PHY_PHYSCSR_SPEED_Msk (7UL << 2) +#define PHY_PHYSCSR_10HD (1UL << 2) +#define PHY_PHYSCSR_10FD (5UL << 2) +#define PHY_PHYSCSR_100HD (2UL << 2) +#define PHY_PHYSCSR_100FD (6UL << 2) + +whal_Error whal_Lan8742a_Init(whal_EthPhy *phyDev) +{ + whal_Lan8742a_Cfg *cfg; + whal_Error err; + uint16_t val; + + if (!phyDev || !phyDev->eth || !phyDev->cfg) + return WHAL_EINVAL; + + cfg = (whal_Lan8742a_Cfg *)phyDev->cfg; +#ifdef WHAL_CFG_NO_TIMEOUT + (void)cfg; +#endif + + /* Software reset */ + err = whal_Eth_MdioWrite(phyDev->eth, phyDev->addr, PHY_BCR, + PHY_BCR_RESET); + if (err) + return err; + + /* Wait for reset to complete (bit self-clears) */ + WHAL_TIMEOUT_START(cfg->timeout); + do { + if (WHAL_TIMEOUT_EXPIRED(cfg->timeout)) + return WHAL_ETIMEOUT; + err = whal_Eth_MdioRead(phyDev->eth, phyDev->addr, PHY_BCR, &val); + if (err) + return err; + } while (val & PHY_BCR_RESET); + + /* Enable autonegotiation */ + err = whal_Eth_MdioRead(phyDev->eth, phyDev->addr, PHY_BCR, &val); + if (err) + return err; + val |= PHY_BCR_ANEN; + err = whal_Eth_MdioWrite(phyDev->eth, phyDev->addr, PHY_BCR, val); + if (err) + return err; + + return WHAL_SUCCESS; +} + +whal_Error whal_Lan8742a_Deinit(whal_EthPhy *phyDev) +{ + if (!phyDev || !phyDev->eth) + return WHAL_EINVAL; + + return WHAL_SUCCESS; +} + +whal_Error whal_Lan8742a_GetLinkState(whal_EthPhy *phyDev, uint8_t *up, + uint8_t *speed, uint8_t *duplex) +{ + whal_Error err; + uint16_t bsr; + uint16_t scsr; + + if (!phyDev || !phyDev->eth || !up || !speed || !duplex) + return WHAL_EINVAL; + + /* + * BSR link bit is latching-low (IEEE 802.3). First read clears a + * stale link-down event; second read gives current status. + */ + err = whal_Eth_MdioRead(phyDev->eth, phyDev->addr, PHY_BSR, &bsr); + if (err) + return err; + err = whal_Eth_MdioRead(phyDev->eth, phyDev->addr, PHY_BSR, &bsr); + if (err) + return err; + + *up = (bsr & PHY_BSR_LINK) ? 1 : 0; + + if (*up) { + err = whal_Eth_MdioRead(phyDev->eth, phyDev->addr, PHY_PHYSCSR, + &scsr); + if (err) + return err; + uint16_t spd = scsr & PHY_PHYSCSR_SPEED_Msk; + if (spd == PHY_PHYSCSR_100FD) { + *speed = 100; + *duplex = 1; + } else if (spd == PHY_PHYSCSR_100HD) { + *speed = 100; + *duplex = 0; + } else if (spd == PHY_PHYSCSR_10FD) { + *speed = 10; + *duplex = 1; + } else { + *speed = 10; + *duplex = 0; + } + } else { + *speed = 0; + *duplex = 0; + } + + return WHAL_SUCCESS; +} + +const whal_EthPhyDriver whal_Lan8742a_Driver = { + .Init = whal_Lan8742a_Init, + .Deinit = whal_Lan8742a_Deinit, + .GetLinkState = whal_Lan8742a_GetLinkState, +}; diff --git a/tests/eth/test_eth.c b/tests/eth/test_eth.c new file mode 100644 index 0000000..5243e69 --- /dev/null +++ b/tests/eth/test_eth.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include "board.h" +#include "test.h" + +/* + * Generic Ethernet tests. + * + * Tests MDIO access by reading standard IEEE 802.3 PHY ID registers + * (registers 2 and 3). These are hardcoded in every PHY and readable + * without a cable or link. + */ + +/* IEEE 802.3 standard PHY registers */ +#define PHY_REG_PHYIDR1 2 +#define PHY_REG_PHYIDR2 3 + +static void Test_Eth_MdioReadPhyId(void) +{ + uint16_t id1 = 0; + uint16_t id2 = 0; + + WHAL_ASSERT_EQ(whal_Eth_MdioRead(&g_whalEth, BOARD_ETH_PHY_ADDR, + PHY_REG_PHYIDR1, &id1), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Eth_MdioRead(&g_whalEth, BOARD_ETH_PHY_ADDR, + PHY_REG_PHYIDR2, &id2), WHAL_SUCCESS); + + /* PHY ID should not be 0x0000 or 0xFFFF (no PHY / bus error) */ + WHAL_ASSERT_NEQ(id1, 0x0000); + WHAL_ASSERT_NEQ(id1, 0xFFFF); + WHAL_ASSERT_NEQ(id2, 0x0000); + WHAL_ASSERT_NEQ(id2, 0xFFFF); + + /* Verify against board-defined expected PHY ID */ + WHAL_ASSERT_EQ(id1, BOARD_ETH_PHY_ID1); + WHAL_ASSERT_EQ(id2, BOARD_ETH_PHY_ID2); +} + + +static void Test_Eth_PhyGetLinkState(void) +{ + uint8_t up = 0; + uint8_t speed = 0; + uint8_t duplex = 0; + + WHAL_ASSERT_EQ(whal_EthPhy_GetLinkState(&g_whalEthPhy, &up, &speed, + &duplex), WHAL_SUCCESS); + + whal_Test_Printf(" link: %s, speed: %d, duplex: %s\n", + up ? "up" : "down", speed, + duplex ? "full" : "half"); +} + +void whal_Test_Eth(void) +{ + WHAL_TEST_SUITE_START("eth"); + WHAL_TEST(Test_Eth_MdioReadPhyId); + WHAL_TEST(Test_Eth_PhyGetLinkState); + WHAL_TEST_SUITE_END(); +} diff --git a/tests/eth/test_stm32h5_eth.c b/tests/eth/test_stm32h5_eth.c new file mode 100644 index 0000000..f5a355d --- /dev/null +++ b/tests/eth/test_stm32h5_eth.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include "board.h" +#include "test.h" + +/* + * STM32H5 Ethernet platform-specific tests. + * + * Uses MAC-internal loopback to test the full send/recv path without + * requiring a cable or link partner. + */ + +static void Test_Eth_Loopback(void) +{ + uint8_t txFrame[64] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x80, 0xE1, 0x00, 0x00, 0x01, + 0x08, 0x00, + 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, + 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, + 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, + 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, + 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, + 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, + 0xDE, 0xAD, + }; + uint8_t rxFrame[1536]; + size_t rxLen = sizeof(rxFrame); + whal_Error err; + + WHAL_ASSERT_EQ(whal_Stm32h5Eth_Ext_EnableLoopback(&g_whalEth, 1), + WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Eth_Start(&g_whalEth, WHAL_ETH_SPEED_100, + WHAL_ETH_DUPLEX_FULL), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Eth_Send(&g_whalEth, txFrame, sizeof(txFrame)), + WHAL_SUCCESS); + + /* Poll for the looped-back frame */ + uint32_t start = g_tick; + do { + err = whal_Eth_Recv(&g_whalEth, rxFrame, &rxLen); + } while (err == WHAL_ENOTREADY && (g_tick - start) < 100); + + WHAL_ASSERT_EQ(err, WHAL_SUCCESS); + + /* Verify payload matches (skip MAC header) */ + WHAL_ASSERT_MEM_EQ(rxFrame + 14, txFrame + 14, sizeof(txFrame) - 14); + + WHAL_ASSERT_EQ(whal_Eth_Stop(&g_whalEth), WHAL_SUCCESS); + + WHAL_ASSERT_EQ(whal_Stm32h5Eth_Ext_EnableLoopback(&g_whalEth, 0), + WHAL_SUCCESS); +} + +void whal_Test_Eth_Platform(void) +{ + WHAL_TEST_SUITE_START("eth_platform"); + WHAL_TEST(Test_Eth_Loopback); + WHAL_TEST_SUITE_END(); +} diff --git a/tests/main.c b/tests/main.c index 96de04d..1ec8d24 100644 --- a/tests/main.c +++ b/tests/main.c @@ -54,6 +54,13 @@ void whal_Test_Crypto(void); void whal_Test_Block(void); #endif +#ifdef WHAL_TEST_ENABLE_ETH +void whal_Test_Eth(void); +#ifdef WHAL_TEST_ENABLE_ETH_PLATFORM +void whal_Test_Eth_Platform(void); +#endif +#endif + int g_whalTestPassed; int g_whalTestFailed; int g_whalTestSkipped; @@ -136,6 +143,13 @@ void main(void) whal_Test_Block(); #endif +#ifdef WHAL_TEST_ENABLE_ETH + whal_Test_Eth(); +#ifdef WHAL_TEST_ENABLE_ETH_PLATFORM + whal_Test_Eth_Platform(); +#endif +#endif + WHAL_TEST_SUMMARY(); if (g_whalTestFailed == 0) { diff --git a/wolfHAL/eth/eth.h b/wolfHAL/eth/eth.h new file mode 100644 index 0000000..49d6077 --- /dev/null +++ b/wolfHAL/eth/eth.h @@ -0,0 +1,184 @@ +#ifndef WHAL_ETH_H +#define WHAL_ETH_H + +#include +#include +#include +#include + +/* + * @file eth.h + * @brief Generic Ethernet MAC abstraction and driver interface. + */ + +typedef struct whal_Eth whal_Eth; + +/* + * @brief Ethernet link speed. + */ +#define WHAL_ETH_SPEED_10 10 +#define WHAL_ETH_SPEED_100 100 + +/* + * @brief Ethernet duplex mode. + */ +#define WHAL_ETH_DUPLEX_HALF 0 +#define WHAL_ETH_DUPLEX_FULL 1 + +/* + * @brief Driver vtable for Ethernet MAC devices. + */ +typedef struct { + /* Initialize the Ethernet MAC, DMA descriptors, and MAC address. */ + whal_Error (*Init)(whal_Eth *ethDev); + /* Deinitialize the Ethernet MAC hardware. */ + whal_Error (*Deinit)(whal_Eth *ethDev); + /* Configure link speed/duplex, enable MAC TX/RX, start DMA engines. */ + whal_Error (*Start)(whal_Eth *ethDev, uint8_t speed, uint8_t duplex); + /* Disable MAC TX/RX and stop DMA engines. */ + whal_Error (*Stop)(whal_Eth *ethDev); + /* Transmit an Ethernet frame. */ + whal_Error (*Send)(whal_Eth *ethDev, const uint8_t *frame, size_t len); + /* Receive an Ethernet frame. */ + whal_Error (*Recv)(whal_Eth *ethDev, uint8_t *frame, size_t *len); + /* Read a PHY register via MDIO. */ + whal_Error (*MdioRead)(whal_Eth *ethDev, uint8_t phyAddr, uint8_t reg, + uint16_t *val); + /* Write a PHY register via MDIO. */ + whal_Error (*MdioWrite)(whal_Eth *ethDev, uint8_t phyAddr, uint8_t reg, + uint16_t val); +} whal_EthDriver; + +/* + * @brief Ethernet device instance tying a register map and driver. + */ +struct whal_Eth { + const whal_Regmap regmap; + const whal_EthDriver *driver; + void *cfg; +}; + +/* + * @brief Initialize an Ethernet device and its driver. + * + * Configures the MAC, sets up DMA descriptor rings, and sets the MAC + * address. Does not enable TX/RX — call Start for that. + * + * @param ethDev Ethernet device instance. + * + * @retval WHAL_SUCCESS Initialization completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +#ifdef WHAL_CFG_DIRECT_CALLBACKS +#define whal_Eth_Init(ethDev) ((ethDev)->driver->Init((ethDev))) +#define whal_Eth_Deinit(ethDev) ((ethDev)->driver->Deinit((ethDev))) +#define whal_Eth_Start(ethDev, speed, duplex) \ + ((ethDev)->driver->Start((ethDev), (speed), (duplex))) +#define whal_Eth_Stop(ethDev) ((ethDev)->driver->Stop((ethDev))) +#define whal_Eth_Send(ethDev, frame, len) \ + ((ethDev)->driver->Send((ethDev), (frame), (len))) +#define whal_Eth_Recv(ethDev, frame, len) \ + ((ethDev)->driver->Recv((ethDev), (frame), (len))) +#define whal_Eth_MdioRead(ethDev, phyAddr, reg, val) \ + ((ethDev)->driver->MdioRead((ethDev), (phyAddr), (reg), (val))) +#define whal_Eth_MdioWrite(ethDev, phyAddr, reg, val) \ + ((ethDev)->driver->MdioWrite((ethDev), (phyAddr), (reg), (val))) +#else +/* + * @brief Initialize an Ethernet device and its driver. + * + * @param ethDev Ethernet device instance. + * + * @retval WHAL_SUCCESS Initialization completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Eth_Init(whal_Eth *ethDev); +/* + * @brief Deinitialize an Ethernet device. + * + * @param ethDev Ethernet device instance. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Eth_Deinit(whal_Eth *ethDev); +/* + * @brief Start the Ethernet MAC. + * + * Configures MAC speed and duplex to match the PHY, enables TX/RX, + * and starts the DMA engines. + * + * @param ethDev Ethernet device instance. + * @param speed Link speed: WHAL_ETH_SPEED_10 or WHAL_ETH_SPEED_100. + * @param duplex Duplex mode: WHAL_ETH_DUPLEX_HALF or WHAL_ETH_DUPLEX_FULL. + * + * @retval WHAL_SUCCESS MAC started. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Eth_Start(whal_Eth *ethDev, uint8_t speed, uint8_t duplex); +/* + * @brief Stop the Ethernet MAC. + * + * Disables TX/RX and stops the DMA engines. + * + * @param ethDev Ethernet device instance. + * + * @retval WHAL_SUCCESS MAC stopped. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Eth_Stop(whal_Eth *ethDev); +/* + * @brief Transmit an Ethernet frame. + * + * @param ethDev Ethernet device instance. + * @param frame Frame data to transmit. + * @param len Length of the frame in bytes. + * + * @retval WHAL_SUCCESS Frame queued for transmission. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ENOTREADY No TX descriptor available. + */ +whal_Error whal_Eth_Send(whal_Eth *ethDev, const uint8_t *frame, size_t len); +/* + * @brief Receive an Ethernet frame. + * + * @param ethDev Ethernet device instance. + * @param frame Buffer to receive frame data into. + * @param len On entry, size of the buffer. On exit, length of received frame. + * + * @retval WHAL_SUCCESS Frame received. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ENOTREADY No frame available. + */ +whal_Error whal_Eth_Recv(whal_Eth *ethDev, uint8_t *frame, size_t *len); +/* + * @brief Read a PHY register via the MDIO bus. + * + * @param ethDev Ethernet device instance (MDIO master). + * @param phyAddr PHY address on the MDIO bus (0-31). + * @param reg PHY register address (0-31). + * @param val Output for the 16-bit register value. + * + * @retval WHAL_SUCCESS Register read completed. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ETIMEOUT MDIO bus busy timeout. + */ +whal_Error whal_Eth_MdioRead(whal_Eth *ethDev, uint8_t phyAddr, uint8_t reg, + uint16_t *val); +/* + * @brief Write a PHY register via the MDIO bus. + * + * @param ethDev Ethernet device instance (MDIO master). + * @param phyAddr PHY address on the MDIO bus (0-31). + * @param reg PHY register address (0-31). + * @param val 16-bit value to write. + * + * @retval WHAL_SUCCESS Register write completed. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ETIMEOUT MDIO bus busy timeout. + */ +whal_Error whal_Eth_MdioWrite(whal_Eth *ethDev, uint8_t phyAddr, uint8_t reg, + uint16_t val); +#endif + +#endif /* WHAL_ETH_H */ diff --git a/wolfHAL/eth/stm32h5_eth.h b/wolfHAL/eth/stm32h5_eth.h new file mode 100644 index 0000000..469429d --- /dev/null +++ b/wolfHAL/eth/stm32h5_eth.h @@ -0,0 +1,183 @@ +#ifndef WHAL_STM32H5_ETH_H +#define WHAL_STM32H5_ETH_H + +#include +#include +#include +#include + +/* + * @file stm32h5_eth.h + * @brief STM32H5 Ethernet MAC driver configuration. + * + * The STM32H5 Ethernet peripheral is a Synopsys DWC Ethernet MAC with + * integrated DMA controller. It supports 10/100 Mbps via MII or RMII + * interface, hardware checksum offload, and IEEE 1588 timestamping. + * + * The driver uses descriptor rings in RAM for TX and RX DMA transfers. + * Descriptor and buffer memory must be provided by the board configuration. + */ + +/* TX DMA descriptor (4 x 32-bit words, 16 bytes) */ +typedef struct { + volatile uint32_t des[4]; +} whal_Stm32h5Eth_TxDesc; + +/* RX DMA descriptor (4 x 32-bit words, 16 bytes) */ +typedef struct { + volatile uint32_t des[4]; +} whal_Stm32h5Eth_RxDesc; + +/* + * @brief STM32H5 Ethernet MAC configuration. + */ +typedef struct whal_Stm32h5Eth_Cfg { + uint8_t macAddr[6]; /* MAC address */ + whal_Stm32h5Eth_TxDesc *txDescs; /* TX descriptor ring (pre-allocated) */ + uint8_t *txBufs; /* TX frame buffers (pre-allocated) */ + size_t txDescCount; /* Number of TX descriptors */ + size_t txBufSize; /* Size of each TX buffer in bytes */ + whal_Stm32h5Eth_RxDesc *rxDescs; /* RX descriptor ring (pre-allocated) */ + uint8_t *rxBufs; /* RX frame buffers (pre-allocated) */ + size_t rxDescCount; /* Number of RX descriptors */ + size_t rxBufSize; /* Size of each RX buffer in bytes */ + whal_Timeout *timeout; + /* Runtime state (set by driver, not by user) */ + size_t txHead; /* Next TX descriptor to use */ + size_t rxHead; /* Next RX descriptor to check */ +} whal_Stm32h5Eth_Cfg; + +/* + * @brief Driver instance for STM32H5 Ethernet MAC. + */ +extern const whal_EthDriver whal_Stm32h5Eth_Driver; + +/* + * @brief Initialize the STM32H5 Ethernet MAC. + * + * Configures the MAC, MTL, and DMA. Sets up descriptor rings and + * MAC address. Does not start TX/RX. + * + * @param ethDev Ethernet device instance. + * + * @retval WHAL_SUCCESS Initialization completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32h5Eth_Init(whal_Eth *ethDev); + +/* + * @brief Deinitialize the STM32H5 Ethernet MAC. + * + * @param ethDev Ethernet device instance. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32h5Eth_Deinit(whal_Eth *ethDev); + +/* + * @brief Start the Ethernet MAC TX/RX and DMA engines. + * + * Configures MAC speed and duplex, then enables TX/RX and starts DMA. + * + * @param ethDev Ethernet device instance. + * @param speed Link speed: WHAL_ETH_SPEED_10 or WHAL_ETH_SPEED_100. + * @param duplex Duplex mode: WHAL_ETH_DUPLEX_HALF or WHAL_ETH_DUPLEX_FULL. + * + * @retval WHAL_SUCCESS MAC started. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32h5Eth_Start(whal_Eth *ethDev, uint8_t speed, + uint8_t duplex); + +/* + * @brief Stop the Ethernet MAC TX/RX and DMA engines. + * + * @param ethDev Ethernet device instance. + * + * @retval WHAL_SUCCESS MAC stopped. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32h5Eth_Stop(whal_Eth *ethDev); + +/* + * @brief Transmit an Ethernet frame. + * + * Copies frame data into the next available TX descriptor buffer, + * sets the OWN bit, and advances the DMA tail pointer. + * + * @param ethDev Ethernet device instance. + * @param frame Frame data to transmit. + * @param len Length of the frame in bytes. + * + * @retval WHAL_SUCCESS Frame queued for transmission. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ENOTREADY No TX descriptor available. + */ +whal_Error whal_Stm32h5Eth_Send(whal_Eth *ethDev, const uint8_t *frame, + size_t len); + +/* + * @brief Receive an Ethernet frame. + * + * Checks if the DMA has completed an RX descriptor. If so, copies + * the frame data into the caller's buffer and returns the descriptor + * to DMA. + * + * @param ethDev Ethernet device instance. + * @param frame Buffer to receive frame data into. + * @param len On entry, buffer size. On exit, received frame length. + * + * @retval WHAL_SUCCESS Frame received. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ENOTREADY No frame available. + */ +whal_Error whal_Stm32h5Eth_Recv(whal_Eth *ethDev, uint8_t *frame, + size_t *len); + +/* + * @brief Read a PHY register via MDIO. + * + * @param ethDev Ethernet device instance. + * @param phyAddr PHY address on the MDIO bus (0-31). + * @param reg PHY register address (0-31). + * @param val Output for the 16-bit register value. + * + * @retval WHAL_SUCCESS Register read completed. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ETIMEOUT MDIO busy timeout. + */ +whal_Error whal_Stm32h5Eth_MdioRead(whal_Eth *ethDev, uint8_t phyAddr, + uint8_t reg, uint16_t *val); + +/* + * @brief Write a PHY register via MDIO. + * + * @param ethDev Ethernet device instance. + * @param phyAddr PHY address on the MDIO bus (0-31). + * @param reg PHY register address (0-31). + * @param val 16-bit value to write. + * + * @retval WHAL_SUCCESS Register write completed. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ETIMEOUT MDIO busy timeout. + */ +whal_Error whal_Stm32h5Eth_MdioWrite(whal_Eth *ethDev, uint8_t phyAddr, + uint8_t reg, uint16_t val); + +/* + * @brief Enable or disable MAC-internal loopback. + * + * When enabled, the TX data path feeds directly into the RX data path + * inside the MAC. No PHY, cable, or link partner is needed. + * + * @param ethDev Ethernet device instance. + * @param enable 1 to enable loopback, 0 to disable. + * + * @retval WHAL_SUCCESS Loopback state changed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32h5Eth_Ext_EnableLoopback(whal_Eth *ethDev, + uint8_t enable); + +#endif /* WHAL_STM32H5_ETH_H */ diff --git a/wolfHAL/eth_phy/eth_phy.h b/wolfHAL/eth_phy/eth_phy.h new file mode 100644 index 0000000..db148d7 --- /dev/null +++ b/wolfHAL/eth_phy/eth_phy.h @@ -0,0 +1,97 @@ +#ifndef WHAL_ETH_PHY_H +#define WHAL_ETH_PHY_H + +#include +#include +#include + +/* + * @file eth_phy.h + * @brief Generic Ethernet PHY abstraction and driver interface. + * + * The PHY driver handles link negotiation and status for an Ethernet + * PHY device connected to a MAC via the MDIO bus. Different PHY chips + * (e.g. LAN8742A, DP83848) have different init sequences but share + * the same API. + */ + +typedef struct whal_EthPhy whal_EthPhy; + +/* + * @brief Driver vtable for Ethernet PHY devices. + */ +typedef struct { + /* Reset PHY, configure autonegotiation, and wait for link. */ + whal_Error (*Init)(whal_EthPhy *phyDev); + /* Power down the PHY. */ + whal_Error (*Deinit)(whal_EthPhy *phyDev); + /* Read current link state: up/down, negotiated speed, and duplex. */ + whal_Error (*GetLinkState)(whal_EthPhy *phyDev, uint8_t *up, + uint8_t *speed, uint8_t *duplex); +} whal_EthPhyDriver; + +/* + * @brief Ethernet PHY device instance. + * + * The PHY is not memory-mapped. It accesses registers through the + * parent MAC's MDIO bus via whal_Eth_MdioRead/MdioWrite. + */ +struct whal_EthPhy { + whal_Eth *eth; /* MAC whose MDIO bus this PHY is on */ + uint8_t addr; /* PHY address on MDIO bus (0-31) */ + const whal_EthPhyDriver *driver; + void *cfg; +}; + +/* + * @brief Initialize an Ethernet PHY. + * + * Resets the PHY, configures autonegotiation, and waits for link. + * + * @param phyDev PHY device instance. + * + * @retval WHAL_SUCCESS PHY initialized and link established. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ETIMEOUT Link did not come up within timeout. + */ +#ifdef WHAL_CFG_DIRECT_CALLBACKS +#define whal_EthPhy_Init(phyDev) ((phyDev)->driver->Init((phyDev))) +#define whal_EthPhy_Deinit(phyDev) ((phyDev)->driver->Deinit((phyDev))) +#define whal_EthPhy_GetLinkState(phyDev, up, speed, duplex) \ + ((phyDev)->driver->GetLinkState((phyDev), (up), (speed), (duplex))) +#else +/* + * @brief Initialize an Ethernet PHY. + * + * @param phyDev PHY device instance. + * + * @retval WHAL_SUCCESS PHY initialized and link established. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ETIMEOUT Link did not come up within timeout. + */ +whal_Error whal_EthPhy_Init(whal_EthPhy *phyDev); +/* + * @brief Deinitialize an Ethernet PHY. + * + * @param phyDev PHY device instance. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_EthPhy_Deinit(whal_EthPhy *phyDev); +/* + * @brief Get the current link state. + * + * @param phyDev PHY device instance. + * @param up Output: 1 if link is up, 0 if down. + * @param speed Output: negotiated speed (WHAL_ETH_SPEED_10/100). + * @param duplex Output: duplex mode (WHAL_ETH_DUPLEX_HALF/FULL). + * + * @retval WHAL_SUCCESS Link state read. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_EthPhy_GetLinkState(whal_EthPhy *phyDev, uint8_t *up, + uint8_t *speed, uint8_t *duplex); +#endif + +#endif /* WHAL_ETH_PHY_H */ diff --git a/wolfHAL/eth_phy/lan8742a.h b/wolfHAL/eth_phy/lan8742a.h new file mode 100644 index 0000000..fb054a7 --- /dev/null +++ b/wolfHAL/eth_phy/lan8742a.h @@ -0,0 +1,66 @@ +#ifndef WHAL_LAN8742A_H +#define WHAL_LAN8742A_H + +#include +#include + +/* + * @file lan8742a.h + * @brief LAN8742A Ethernet PHY driver. + * + * The LAN8742A is a 10/100 Ethernet PHY from Microchip (formerly SMSC) + * commonly found on STM32 Nucleo-144 boards. It connects to the MAC + * via RMII and is configured over the MDIO bus. + */ + +/* + * @brief LAN8742A PHY configuration. + */ +typedef struct whal_Lan8742a_Cfg { + whal_Timeout *timeout; +} whal_Lan8742a_Cfg; + +/* + * @brief Driver instance for LAN8742A PHY. + */ +extern const whal_EthPhyDriver whal_Lan8742a_Driver; + +/* + * @brief Initialize the LAN8742A PHY. + * + * Resets the PHY, configures autonegotiation, and waits for link. + * + * @param phyDev PHY device instance. + * + * @retval WHAL_SUCCESS PHY initialized. + * @retval WHAL_EINVAL Invalid arguments. + * @retval WHAL_ETIMEOUT Link did not come up. + */ +whal_Error whal_Lan8742a_Init(whal_EthPhy *phyDev); + +/* + * @brief Deinitialize the LAN8742A PHY. + * + * Powers down the PHY. + * + * @param phyDev PHY device instance. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Lan8742a_Deinit(whal_EthPhy *phyDev); + +/* + * @brief Get the LAN8742A link state. + * + * @param phyDev PHY device instance. + * @param up Output: 1 if link is up, 0 if down. + * @param speed Output: negotiated speed (10 or 100). + * + * @retval WHAL_SUCCESS Link state read. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Lan8742a_GetLinkState(whal_EthPhy *phyDev, uint8_t *up, + uint8_t *speed, uint8_t *duplex); + +#endif /* WHAL_LAN8742A_H */ diff --git a/wolfHAL/platform/st/stm32h563xx.h b/wolfHAL/platform/st/stm32h563xx.h index dd23bb5..57f4b81 100644 --- a/wolfHAL/platform/st/stm32h563xx.h +++ b/wolfHAL/platform/st/stm32h563xx.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include /* * @file stm32h563xx.h @@ -179,4 +181,37 @@ }, \ .driver = &whal_Stm32h5Flash_Driver +/* RCC_APB3ENR (offset 0x0A8), bit 1 */ +#define WHAL_STM32H563_SBS_CLOCK \ + .regOffset = 0x0A8, \ + .enableMask = (1UL << 1), \ + .enablePos = 1 + +/* Ethernet device macros */ + +#define WHAL_STM32H563_ETH_DEVICE \ + .regmap = { \ + .base = 0x40028000, \ + .size = 0x1200, \ + }, \ + .driver = &whal_Stm32h5Eth_Driver + +/* RCC_AHB1ENR (offset 0x088), bit 19 */ +#define WHAL_STM32H563_ETH_CLOCK \ + .regOffset = 0x088, \ + .enableMask = (1UL << 19), \ + .enablePos = 19 + +/* RCC_AHB1ENR (offset 0x088), bit 20 — ETH TX clock */ +#define WHAL_STM32H563_ETHTX_CLOCK \ + .regOffset = 0x088, \ + .enableMask = (1UL << 20), \ + .enablePos = 20 + +/* RCC_AHB1ENR (offset 0x088), bit 21 — ETH RX clock */ +#define WHAL_STM32H563_ETHRX_CLOCK \ + .regOffset = 0x088, \ + .enableMask = (1UL << 21), \ + .enablePos = 21 + #endif /* WHAL_STM32H563XX_H */ diff --git a/wolfHAL/wolfHAL.h b/wolfHAL/wolfHAL.h index f23cfaa..25836bc 100644 --- a/wolfHAL/wolfHAL.h +++ b/wolfHAL/wolfHAL.h @@ -21,5 +21,7 @@ #include #include #include +#include +#include #endif /* WOLFHAL_H */