diff --git a/drivers/ethernet/Kconfig b/drivers/ethernet/Kconfig index dbcfcbd1a51..af47bda529b 100644 --- a/drivers/ethernet/Kconfig +++ b/drivers/ethernet/Kconfig @@ -38,5 +38,6 @@ source "drivers/ethernet/Kconfig.enc28j60" source "drivers/ethernet/Kconfig.mcux" source "drivers/ethernet/Kconfig.dw" source "drivers/ethernet/Kconfig.sam_gmac" +source "drivers/ethernet/Kconfig.stm32_hal" endmenu diff --git a/drivers/ethernet/Kconfig.stm32_hal b/drivers/ethernet/Kconfig.stm32_hal new file mode 100644 index 00000000000..2344de03b7b --- /dev/null +++ b/drivers/ethernet/Kconfig.stm32_hal @@ -0,0 +1,76 @@ +# Kconfig - STM32 HAL Ethernet driver configuration options + +# +# Copyright (c) 2017 Erwin Rol +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig ETH_STM32_HAL + bool + prompt "STM32 HAL Ethernet driver" + depends on NET_L2_ETHERNET + default n + help + Enable STM32 HAL based Ethernet driver. + +if ETH_STM32_HAL + +config ETH_STM32_HAL_NAME + string "Device name" + default "ETH_0" + +config ETH_STM32_HAL_IRQ_PRI + int "Controller interrupt priority" + depends on ETH_STM32_HAL + default 0 + help + IRQ priority + +config ETH_STM32_HAL_RX_THREAD_STACK_SIZE + int "RX thread stack size" + depends on ETH_STM32_HAL + default 1500 + help + RX thread stack size + +config ETH_STM32_HAL_RX_THREAD_PRIO + int "RX thread priority" + depends on ETH_STM32_HAL + default 2 + help + RX thread priority + +config ETH_STM32_HAL_RANDOM_MAC + bool "Random MAC address" + depends on ETH_STM32_HAL && RANDOM_GENERATOR + default y + help + Generate a random MAC address dynamically. + +if !ETH_STM32_HAL_RANDOM_MAC + +config ETH_STM32_HAL_MAC3 + hex "MAC Address Byte 3" + default 0 + range 0 ff + help + This is the byte 3 of the MAC address. + +config ETH_STM32_HAL_MAC4 + hex "MAC Address Byte 4" + default 0 + range 0 ff + help + This is the byte 4 of the MAC address. + +config ETH_STM32_HAL_MAC5 + hex "MAC Address Byte 5" + default 0 + range 0 ff + help + This is the byte 5 of the MAC address. +endif + +endif # ETH_STM32_HAL + diff --git a/drivers/ethernet/Makefile b/drivers/ethernet/Makefile index 635058f4201..fa629514b06 100644 --- a/drivers/ethernet/Makefile +++ b/drivers/ethernet/Makefile @@ -4,3 +4,5 @@ obj-$(CONFIG_ETH_SAM_GMAC) += eth_sam_gmac.o phy_sam_gmac.o obj-$(CONFIG_ETH_DW) += eth_dw.o obj-$(CONFIG_ETH_ENC28J60) += eth_enc28j60.o obj-$(CONFIG_ETH_MCUX) += eth_mcux.o +obj-$(CONFIG_ETH_STM32_HAL) += eth_stm32_hal.o + diff --git a/drivers/ethernet/eth_stm32_hal.c b/drivers/ethernet/eth_stm32_hal.c new file mode 100644 index 00000000000..5e9b87bae82 --- /dev/null +++ b/drivers/ethernet/eth_stm32_hal.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2017 Erwin Rol + * SPDX-License-Identifier: Apache-2.0 + */ + +#define SYS_LOG_DOMAIN "dev/eth_stm32_hal" +#define SYS_LOG_LEVEL CONFIG_SYS_LOG_ETHERNET_LEVEL +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eth_stm32_hal_priv.h" + +static ETH_DMADescTypeDef dma_rx_desc_tab[ETH_RXBUFNB] __aligned(4); +static ETH_DMADescTypeDef dma_tx_desc_tab[ETH_TXBUFNB] __aligned(4); +static u8_t dma_rx_buffer[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __aligned(4); +static u8_t dma_tx_buffer[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __aligned(4); + +static int eth_tx(struct net_if *iface, struct net_pkt *pkt) +{ + struct device *dev; + struct eth_stm32_hal_dev_data *dev_data; + ETH_HandleTypeDef *heth; + u8_t *dma_buffer; + int res; + struct net_buf *frag; + u16_t total_len; + __IO ETH_DMADescTypeDef *dma_tx_desc; + + __ASSERT_NO_MSG(iface != NULL); + __ASSERT_NO_MSG(pkt != NULL); + __ASSERT_NO_MSG(pkt->frags != NULL); + + dev = net_if_get_device(iface); + dev_data = DEV_DATA(dev); + + __ASSERT_NO_MSG(dev != NULL); + __ASSERT_NO_MSG(dev_data != NULL); + + heth = &dev_data->heth; + + k_mutex_lock(&dev_data->tx_mutex, K_FOREVER); + + total_len = net_pkt_ll_reserve(pkt) + net_pkt_get_len(pkt); + if (total_len > ETH_TX_BUF_SIZE) { + SYS_LOG_ERR("PKT to big\n"); + res = -EIO; + goto error; + } + + dma_tx_desc = heth->TxDesc; + while ((dma_tx_desc->Status & ETH_DMATXDESC_OWN) != (u32_t)RESET) { + k_yield(); + } + + dma_buffer = (u8_t *)(dma_tx_desc->Buffer1Addr); + + memcpy(dma_buffer, net_pkt_ll(pkt), + net_pkt_ll_reserve(pkt) + pkt->frags->len); + dma_buffer += net_pkt_ll_reserve(pkt) + pkt->frags->len; + + frag = pkt->frags->frags; + while (frag) { + memcpy(dma_buffer, frag->data, frag->len); + dma_buffer += frag->len; + frag = frag->frags; + } + + if (HAL_ETH_TransmitFrame(heth, total_len) != HAL_OK) { + SYS_LOG_ERR("HAL_ETH_TransmitFrame failed\n"); + res = -EIO; + goto error; + } + + /* When Transmit Underflow flag is set, clear it and issue a + * Transmit Poll Demand to resume transmission. + */ + if ((heth->Instance->DMASR & ETH_DMASR_TUS) != (u32_t)RESET) { + /* Clear TUS ETHERNET DMA flag */ + heth->Instance->DMASR = ETH_DMASR_TUS; + /* Resume DMA transmission*/ + heth->Instance->DMATPDR = 0; + res = -EIO; + goto error; + } + + net_pkt_unref(pkt); + + res = 0; +error: + k_mutex_unlock(&dev_data->tx_mutex); + + return res; +} + +static struct net_pkt *eth_rx(struct device *dev) +{ + struct eth_stm32_hal_dev_data *dev_data; + ETH_HandleTypeDef *heth; + __IO ETH_DMADescTypeDef *dma_rx_desc; + struct net_pkt *pkt; + u16_t total_len; + u8_t *dma_buffer; + int i; + + __ASSERT_NO_MSG(dev != NULL); + + dev_data = DEV_DATA(dev); + + __ASSERT_NO_MSG(dev_data != NULL); + + heth = &dev_data->heth; + + if (HAL_ETH_GetReceivedFrame_IT(heth) != HAL_OK) { + /* no frame available */ + return NULL; + } + + total_len = heth->RxFrameInfos.length; + dma_buffer = (u8_t *)heth->RxFrameInfos.buffer; + + pkt = net_pkt_get_reserve_rx(0, K_NO_WAIT); + if (!pkt) { + SYS_LOG_ERR("Failed to obtain RX buffer"); + goto release_desc; + } + + if (!net_pkt_append_all(pkt, total_len, dma_buffer, K_NO_WAIT)) { + SYS_LOG_ERR("Failed to append RX buffer to context buffer"); + net_pkt_unref(pkt); + pkt = NULL; + goto release_desc; + } + +release_desc: + /* Release descriptors to DMA */ + /* Point to first descriptor */ + dma_rx_desc = heth->RxFrameInfos.FSRxDesc; + /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + for (i = 0; i < heth->RxFrameInfos.SegCount; i++) { + dma_rx_desc->Status |= ETH_DMARXDESC_OWN; + dma_rx_desc = (ETH_DMADescTypeDef *) + (dma_rx_desc->Buffer2NextDescAddr); + } + + /* Clear Segment_Count */ + heth->RxFrameInfos.SegCount = 0; + + /* When Rx Buffer unavailable flag is set: clear it + * and resume reception. + */ + if ((heth->Instance->DMASR & ETH_DMASR_RBUS) != (u32_t)RESET) { + /* Clear RBUS ETHERNET DMA flag */ + heth->Instance->DMASR = ETH_DMASR_RBUS; + /* Resume DMA reception */ + heth->Instance->DMARPDR = 0; + } + + return pkt; +} + +static void rx_thread(void *arg1, void *unused1, void *unused2) +{ + struct device *dev; + struct eth_stm32_hal_dev_data *dev_data; + struct net_pkt *pkt; + int res; + + __ASSERT_NO_MSG(arg1 != NULL); + ARG_UNUSED(unused1); + ARG_UNUSED(unused2); + + dev = (struct device *)arg1; + dev_data = DEV_DATA(dev); + + __ASSERT_NO_MSG(dev_data != NULL); + + while (1) { + k_sem_take(&dev_data->rx_int_sem, K_FOREVER); + + while ((pkt = eth_rx(dev)) != NULL) { + net_pkt_print_frags(pkt); + res = net_recv_data(dev_data->iface, pkt); + if (res < 0) { + SYS_LOG_ERR("Failed to enqueue frame " + "into RX queue: %d", res); + net_pkt_unref(pkt); + } + } + } +} + +static void eth_isr(void *arg) +{ + struct device *dev; + struct eth_stm32_hal_dev_data *dev_data; + ETH_HandleTypeDef *heth; + + __ASSERT_NO_MSG(arg != NULL); + + dev = (struct device *)arg; + dev_data = DEV_DATA(dev); + + __ASSERT_NO_MSG(dev_data != NULL); + + heth = &dev_data->heth; + + __ASSERT_NO_MSG(heth != NULL); + + HAL_ETH_IRQHandler(heth); +} + + +void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth_handle) +{ + __ASSERT_NO_MSG(heth_handle != NULL); + + struct eth_stm32_hal_dev_data *dev_data = + CONTAINER_OF(heth_handle, struct eth_stm32_hal_dev_data, heth); + + __ASSERT_NO_MSG(dev_data != NULL); + + k_sem_give(&dev_data->rx_int_sem); +} + +static int eth_initialize(struct device *dev) +{ + struct eth_stm32_hal_dev_data *dev_data; + struct eth_stm32_hal_dev_cfg *cfg; + + __ASSERT_NO_MSG(dev != NULL); + + dev_data = DEV_DATA(dev); + cfg = DEV_CFG(dev); + + __ASSERT_NO_MSG(dev_data != NULL); + __ASSERT_NO_MSG(cfg != NULL); + + dev_data->clock = device_get_binding(STM32_CLOCK_CONTROL_NAME); + __ASSERT_NO_MSG(dev_data->clock != NULL); + + /* enable clock */ + clock_control_on(dev_data->clock, + (clock_control_subsys_t *)&cfg->pclken); + clock_control_on(dev_data->clock, + (clock_control_subsys_t *)&cfg->pclken_tx); + clock_control_on(dev_data->clock, + (clock_control_subsys_t *)&cfg->pclken_rx); + clock_control_on(dev_data->clock, + (clock_control_subsys_t *)&cfg->pclken_ptp); + + __ASSERT_NO_MSG(cfg->config_func != NULL); + + cfg->config_func(); + + return 0; +} + +#if defined(CONFIG_ETH_STM32_HAL_RANDOM_MAC) +static void generate_mac(u8_t *mac_addr) +{ + u32_t entropy; + + entropy = sys_rand32_get(); + + mac_addr[3] = entropy >> 8; + mac_addr[4] = entropy >> 16; + /* Locally administered, unicast */ + mac_addr[5] = ((entropy >> 0) & 0xfc) | 0x02; +} +#endif + +static void eth0_iface_init(struct net_if *iface) +{ + struct device *dev; + struct eth_stm32_hal_dev_data *dev_data; + ETH_HandleTypeDef *heth; + + __ASSERT_NO_MSG(iface != NULL); + + dev = net_if_get_device(iface); + __ASSERT_NO_MSG(dev != NULL); + + dev_data = DEV_DATA(dev); + __ASSERT_NO_MSG(dev_data != NULL); + + heth = &dev_data->heth; + + dev_data->iface = iface; + + /* Initialize semaphores */ + k_mutex_init(&dev_data->tx_mutex); + k_sem_init(&dev_data->rx_int_sem, 0, UINT_MAX); + + /* Start interruption-poll thread */ + k_thread_create(&dev_data->rx_thread, dev_data->rx_thread_stack, + K_THREAD_STACK_SIZEOF(dev_data->rx_thread_stack), + rx_thread, (void *) dev, NULL, NULL, + K_PRIO_COOP(CONFIG_ETH_STM32_HAL_RX_THREAD_PRIO), + 0, K_NO_WAIT); + +#if defined(CONFIG_ETH_STM32_HAL_RANDOM_MAC) + generate_mac(dev_data->mac_addr); +#endif + + heth->Init.MACAddr = dev_data->mac_addr; + + if (HAL_ETH_Init(heth) != HAL_OK) { + SYS_LOG_ERR("HAL_ETH_Init failed\n"); + } + + HAL_ETH_DMATxDescListInit(heth, dma_tx_desc_tab, + &dma_tx_buffer[0][0], ETH_TXBUFNB); + HAL_ETH_DMARxDescListInit(heth, dma_rx_desc_tab, + &dma_rx_buffer[0][0], ETH_RXBUFNB); + + HAL_ETH_Start(heth); + + /* Register Ethernet MAC Address with the upper layer */ + net_if_set_link_addr(iface, dev_data->mac_addr, + sizeof(dev_data->mac_addr), + NET_LINK_ETHERNET); +} + +static struct net_if_api eth0_api = { + .init = eth0_iface_init, + .send = eth_tx, +}; + +static struct device DEVICE_NAME_GET(eth0_stm32_hal); + +static void eth0_irq_config(void) +{ + IRQ_CONNECT(ETH_IRQn, CONFIG_ETH_STM32_HAL_IRQ_PRI, eth_isr, + DEVICE_GET(eth0_stm32_hal), 0); + irq_enable(ETH_IRQn); +} + +static const struct eth_stm32_hal_dev_cfg eth0_config = { + .config_func = eth0_irq_config, + .pclken = { .bus = STM32_CLOCK_BUS_AHB1, + .enr = LL_AHB1_GRP1_PERIPH_ETHMAC }, + .pclken_tx = { .bus = STM32_CLOCK_BUS_AHB1, + .enr = LL_AHB1_GRP1_PERIPH_ETHMACTX }, + .pclken_rx = { .bus = STM32_CLOCK_BUS_AHB1, + .enr = LL_AHB1_GRP1_PERIPH_ETHMACRX }, + .pclken_ptp = { .bus = STM32_CLOCK_BUS_AHB1, + .enr = LL_AHB1_GRP1_PERIPH_ETHMACPTP }, +}; + +static struct eth_stm32_hal_dev_data eth0_data = { + .heth = { + .Instance = ETH, + .Init = { + .AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE, + .PhyAddress = 0, + .RxMode = ETH_RXINTERRUPT_MODE, + .ChecksumMode = ETH_CHECKSUM_BY_SOFTWARE, + .MediaInterface = ETH_MEDIA_INTERFACE_RMII, + }, + }, + .mac_addr = { + /* ST's OUI */ + 0x00, + 0x80, + 0xE1, +#if !defined(CONFIG_ETH_STM32_HAL_RANDOM_MAC) + CONFIG_ETH_STM32_HAL_MAC3, + CONFIG_ETH_STM32_HAL_MAC4, + CONFIG_ETH_STM32_HAL_MAC5 +#endif + }, +}; + +NET_DEVICE_INIT(eth0_stm32_hal, CONFIG_ETH_STM32_HAL_NAME, eth_initialize, + ð0_data, ð0_config, CONFIG_ETH_INIT_PRIORITY, ð0_api, + ETHERNET_L2, NET_L2_GET_CTX_TYPE(ETHERNET_L2), ETH_STM32_HAL_MTU); + diff --git a/drivers/ethernet/eth_stm32_hal_priv.h b/drivers/ethernet/eth_stm32_hal_priv.h new file mode 100644 index 00000000000..806380f4b0f --- /dev/null +++ b/drivers/ethernet/eth_stm32_hal_priv.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 Erwin Rol + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _ETH_STM32_HAL_PRIV_H_ +#define _ETH_STM32_HAL_PRIV_H_ + +#include +#include + +#define ETH_STM32_HAL_MTU 1500 +#define ETH_STM32_HAL_FRAME_SIZE_MAX (ETH_STM32_HAL_MTU + 18) + +/* Definition of the Ethernet driver buffers size and count */ +#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for receive */ +#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for transmit */ + +/* Device constant configuration parameters */ +struct eth_stm32_hal_dev_cfg { + void (*config_func)(void); + struct stm32_pclken pclken; + struct stm32_pclken pclken_rx; + struct stm32_pclken pclken_tx; + struct stm32_pclken pclken_ptp; +}; + +/* Device run time data */ +struct eth_stm32_hal_dev_data { + struct net_if *iface; + u8_t mac_addr[6]; + ETH_HandleTypeDef heth; + /* clock device */ + struct device *clock; + struct k_mutex tx_mutex; + struct k_sem rx_int_sem; + K_THREAD_STACK_MEMBER(rx_thread_stack, + CONFIG_ETH_STM32_HAL_RX_THREAD_STACK_SIZE); + struct k_thread rx_thread; +}; + +#define DEV_CFG(dev) \ + ((struct eth_stm32_hal_dev_cfg *)(dev)->config->config_info) +#define DEV_DATA(dev) \ + ((struct eth_stm32_hal_dev_data *)(dev)->driver_data) + +#endif /* _ETH_STM32_HAL_PRIV_H_ */ + diff --git a/ext/hal/st/stm32cube/Kbuild b/ext/hal/st/stm32cube/Kbuild index a87cb0a281a..f8514cf8c7b 100644 --- a/ext/hal/st/stm32cube/Kbuild +++ b/ext/hal/st/stm32cube/Kbuild @@ -28,6 +28,7 @@ obj-$(CONFIG_SERIAL_HAS_DRIVER) += stm32f4xx/drivers/src/stm32f4xx_hal_uart.o obj-$(CONFIG_I2C) += stm32f4xx/drivers/src/stm32f4xx_ll_i2c.o obj-$(CONFIG_SPI_STM32) += stm32f4xx/drivers/src/stm32f4xx_ll_spi.o obj-$(CONFIG_RANDOM_STM32_RNG) += stm32f4xx/drivers/src/stm32f4xx_ll_rng.o +obj-$(CONFIG_ETH_STM32_HAL) += stm32f4xx/drivers/src/stm32f4xx_hal_eth.o obj-y += stm32f4xx/soc/system_stm32f4xx.o endif