/* * Copyright (c) 2017 Erwin Rol * SPDX-License-Identifier: Apache-2.0 */ #define LOG_MODULE_NAME eth_stm32_hal #define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL #include LOG_MODULE_REGISTER(LOG_MODULE_NAME); #include #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 inline void disable_mcast_filter(ETH_HandleTypeDef *heth) { __ASSERT_NO_MSG(heth != NULL); u32_t tmp = heth->Instance->MACFFR; /* disable multicast filtering */ tmp &= ~(ETH_MULTICASTFRAMESFILTER_PERFECTHASHTABLE | ETH_MULTICASTFRAMESFILTER_HASHTABLE | ETH_MULTICASTFRAMESFILTER_PERFECT); /* enable receiving all multicast frames */ tmp |= ETH_MULTICASTFRAMESFILTER_NONE; heth->Instance->MACFFR = tmp; /* Wait until the write operation will be taken into account: * at least four TX_CLK/RX_CLK clock cycles */ tmp = heth->Instance->MACFFR; k_sleep(1); heth->Instance->MACFFR = tmp; } static int eth_tx(struct device *dev, struct net_pkt *pkt) { struct eth_stm32_hal_dev_data *dev_data = DEV_DATA(dev); ETH_HandleTypeDef *heth; u8_t *dma_buffer; int res; u16_t total_len; __IO ETH_DMADescTypeDef *dma_tx_desc; __ASSERT_NO_MSG(pkt != NULL); __ASSERT_NO_MSG(pkt->frags != NULL); __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_get_len(pkt); if (total_len > ETH_TX_BUF_SIZE) { LOG_ERR("PKT to big"); 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); if (net_pkt_read(pkt, dma_buffer, total_len)) { res = -EIO; goto error; } if (HAL_ETH_TransmitFrame(heth, total_len) != HAL_OK) { LOG_ERR("HAL_ETH_TransmitFrame failed"); 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; } 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_rx_alloc_with_buffer(dev_data->iface, total_len, AF_UNSPEC, 0, K_NO_WAIT); if (!pkt) { LOG_ERR("Failed to obtain RX buffer"); goto release_desc; } if (net_pkt_write(pkt, dma_buffer, total_len)) { 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; } if (!pkt) { eth_stats_update_errors_rx(dev_data->iface); } 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) { eth_stats_update_errors_rx(dev_data->iface); 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; int ret = 0; __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 */ ret = clock_control_on(dev_data->clock, (clock_control_subsys_t *)&cfg->pclken); ret |= clock_control_on(dev_data->clock, (clock_control_subsys_t *)&cfg->pclken_tx); ret |= clock_control_on(dev_data->clock, (clock_control_subsys_t *)&cfg->pclken_rx); ret |= clock_control_on(dev_data->clock, (clock_control_subsys_t *)&cfg->pclken_ptp); if (ret) { LOG_ERR("Failed to enable ethernet clock"); return -EIO; } __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 eth_iface_init(struct net_if *iface) { struct device *dev; struct eth_stm32_hal_dev_data *dev_data; ETH_HandleTypeDef *heth; u8_t hal_ret; __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; #if defined(CONFIG_ETH_STM32_HAL_RANDOM_MAC) generate_mac(dev_data->mac_addr); #endif heth->Init.MACAddr = dev_data->mac_addr; hal_ret = HAL_ETH_Init(heth); if (hal_ret == HAL_TIMEOUT) { /* HAL Init time out. This could be linked to */ /* a recoverable error. Log the issue and continue */ /* driver initialisation */ LOG_ERR("HAL_ETH_Init Timed out"); } else if (hal_ret != HAL_OK) { LOG_ERR("HAL_ETH_Init failed: %d", hal_ret); return; } /* 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); 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); disable_mcast_filter(heth); LOG_DBG("MAC %02x:%02x:%02x:%02x:%02x:%02x", dev_data->mac_addr[0], dev_data->mac_addr[1], dev_data->mac_addr[2], dev_data->mac_addr[3], dev_data->mac_addr[4], dev_data->mac_addr[5]); /* 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); ethernet_init(iface); } static enum ethernet_hw_caps eth_stm32_hal_get_capabilities(struct device *dev) { ARG_UNUSED(dev); return ETHERNET_LINK_10BASE_T | ETHERNET_LINK_100BASE_T; } static const struct ethernet_api eth_api = { .iface_api.init = eth_iface_init, .get_capabilities = eth_stm32_hal_get_capabilities, .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 = CONFIG_ETH_STM32_HAL_PHY_ADDRESS, .RxMode = ETH_RXINTERRUPT_MODE, .ChecksumMode = ETH_CHECKSUM_BY_SOFTWARE, #if defined(CONFIG_ETH_STM32_HAL_MII) .MediaInterface = ETH_MEDIA_INTERFACE_MII, #else .MediaInterface = ETH_MEDIA_INTERFACE_RMII, #endif }, }, .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, ð_api, ETHERNET_L2, NET_L2_GET_CTX_TYPE(ETHERNET_L2), ETH_STM32_HAL_MTU);