From c4be0c51de0d794849683b12719af024ce28d160 Mon Sep 17 00:00:00 2001 From: Esteban Valverde Date: Mon, 22 Aug 2022 11:13:54 +0100 Subject: [PATCH] drivers: ethernet: adding support for Cyclone V SoC DK ethernet Adding support for Cyclone V SoC ethernet device. Signed-off-by: Esteban Valverde --- drivers/ethernet/CMakeLists.txt | 1 + drivers/ethernet/Kconfig | 1 + drivers/ethernet/Kconfig.cyclonev | 35 + drivers/ethernet/eth_cyclonev.c | 1170 ++++++++++++++++++++++++++ drivers/ethernet/eth_cyclonev_priv.h | 504 +++++++++++ drivers/ethernet/phy_cyclonev.c | 371 ++++++++ 6 files changed, 2082 insertions(+) create mode 100644 drivers/ethernet/Kconfig.cyclonev create mode 100644 drivers/ethernet/eth_cyclonev.c create mode 100644 drivers/ethernet/eth_cyclonev_priv.h create mode 100644 drivers/ethernet/phy_cyclonev.c diff --git a/drivers/ethernet/CMakeLists.txt b/drivers/ethernet/CMakeLists.txt index 9d3133390e7..578a589ab35 100644 --- a/drivers/ethernet/CMakeLists.txt +++ b/drivers/ethernet/CMakeLists.txt @@ -28,6 +28,7 @@ zephyr_library_sources_ifdef(CONFIG_ETH_STELLARIS eth_stellaris.c) zephyr_library_sources_ifdef(CONFIG_ETH_STM32_HAL eth_stm32_hal.c) zephyr_library_sources_ifdef(CONFIG_ETH_W5500 eth_w5500.c) zephyr_library_sources_ifdef(CONFIG_ETH_SAM_GMAC eth_sam_gmac.c) +zephyr_library_sources_ifdef(CONFIG_ETH_CYCLONEV eth_cyclonev.c) if(CONFIG_ETH_NATIVE_POSIX) set(native_posix_source_files eth_native_posix.c eth_native_posix_adapt.c) diff --git a/drivers/ethernet/Kconfig b/drivers/ethernet/Kconfig index 5f2b5f09ee5..643c8ea8284 100644 --- a/drivers/ethernet/Kconfig +++ b/drivers/ethernet/Kconfig @@ -55,6 +55,7 @@ source "drivers/ethernet/Kconfig.gecko" source "drivers/ethernet/Kconfig.w5500" source "drivers/ethernet/Kconfig.dsa" source "drivers/ethernet/Kconfig.xlnx_gem" +source "drivers/ethernet/Kconfig.cyclonev" source "drivers/ethernet/phy/Kconfig" diff --git a/drivers/ethernet/Kconfig.cyclonev b/drivers/ethernet/Kconfig.cyclonev new file mode 100644 index 00000000000..41e563a9b1d --- /dev/null +++ b/drivers/ethernet/Kconfig.cyclonev @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (C) 2022, Intel Corporation +# Description: +# Cyclone V ethernet configuration options + +menuconfig ETH_CYCLONEV + bool "Cyclone V ethernet MAC driver" + default y + depends on DT_HAS_SNPS_ETHERNET_CYCLONEV_ENABLED + +if ETH_CYCLONEV + +config ETH_CVSX_NB_TX_DESCS + int "Number of entries in the transmit descriptor ring" + default 32 + range 4 128 + help + A higher number allows for more packets to be queued which may + improve throughput, but that requires more transient memory. + However there must be enough descriptors to hold all fragments + of a full-size packet to be transmitted or the packet will be + dropped. + + Fragment size is influenced by CONFIG_NET_BUF_DATA_SIZE. + +config ETH_CVSX_NB_RX_DESCS + int "Number of entries in the receive descriptor ring" + default 32 + range 4 128 + help + Received packets are spread across the required number of fragment + buffers. Each RX fragment has a size of CONFIG_NET_BUF_DATA_SIZE. + There is one descriptor entry per fragment. + +endif # ETH_CYCLONEV diff --git a/drivers/ethernet/eth_cyclonev.c b/drivers/ethernet/eth_cyclonev.c new file mode 100644 index 00000000000..94bed3cd867 --- /dev/null +++ b/drivers/ethernet/eth_cyclonev.c @@ -0,0 +1,1170 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2022, Intel Corporation + * Description: + * 3504-0 Universal 10/100/1000 Ethernet MAC (DWC_gmac) + * Driver specifically designed for Cyclone V SoC DevKit use only. + * + * based on Intel SOC FPGA HWLIB Repo + * https://github.com/altera-opensource/intel-socfpga-hwlib + */ + +#define LOG_MODULE_NAME eth_cyclonev +#define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#define DT_DRV_COMPAT snps_ethernet_cyclonev + +#include "eth_cyclonev_priv.h" + +#include + +#include +#include +#include + +#include "phy_cyclonev.c" +#include +#include +#define TX_AVAIL_WAIT K_MSEC(1) +#define INC_WRAP(idx, size) ({ idx = (idx + 1) % size; }) + +static const uint8_t eth_cyclonev_mac_addr[6] = DT_INST_PROP(0, local_mac_address); + +void eth_cyclonev_reset(uint32_t instance); +void eth_cyclonev_set_mac_addr(uint8_t *address, uint32_t instance, uint32_t n, + struct eth_cyclonev_priv *p); +int eth_cyclonev_get_software_reset_status(uint32_t instance, struct eth_cyclonev_priv *p); +int eth_cyclonev_software_reset(uint32_t instance, struct eth_cyclonev_priv *p); +void eth_cyclonev_setup_rxdesc(struct eth_cyclonev_priv *p); +void eth_cyclonev_setup_txdesc(struct eth_cyclonev_priv *p); +static void eth_cyclonev_iface_init(struct net_if *iface); +static int eth_cyclonev_send(const struct device *dev, struct net_pkt *pkt); +void eth_cyclonev_isr(const struct device *dev); +int set_mac_conf_status(int instance, uint32_t *mac_config_reg_settings, + struct eth_cyclonev_priv *p); +int eth_cyclonev_probe(const struct device *dev); +static int eth_cyclonev_start(const struct device *dev); +static int eth_cyclonev_stop(const struct device *dev); +static void eth_cyclonev_receive(struct eth_cyclonev_priv *p); +static void eth_cyclonev_tx_release(struct eth_cyclonev_priv *p); +static int eth_cyclonev_set_config(const struct device *dev, enum ethernet_config_type type, + const struct ethernet_config *config); +static enum ethernet_hw_caps eth_cyclonev_caps(const struct device *dev); + +/** Device config */ +struct eth_cyclonev_config { + /** BBRAM base address */ + uint8_t *base; + /** BBRAM size (Unit:bytes) */ + int size; + int inst_num; + void (*irq_config)(void); +}; + +/** + * @brief Reset gmac device function + * Function initialise HPS interface, see + * https://www.intel.com/content/dam/www/programmable + * /us/en/pdfs/literature/hb/cyclone-v/cv_54001.pdf p. 1252 + * + * @param instance Number of instance (0 or 1 in Cyclone V HPS) + */ + +void eth_cyclonev_reset(uint32_t instance) +{ + /* 1. After the HPS is released from cold or warm reset, + *reset the Ethernet Controller module by setting the appropriate + *emac bit in the permodrst register in the Reset Manager. + */ + + sys_set_bits(RSTMGR_PERMODRST_ADDR, Rstmgr_Permodrst_Emac_Set_Msk[instance]); + + /* 4a. Set the physel_* field in the ctrl register of the System Manager + *(EMAC Group) to 0x1 to select the RGMII PHY interface. + */ + + alt_replbits_word(SYSMGR_EMAC_ADDR, Sysmgr_Core_Emac_Phy_Intf_Sel_Set_Msk[instance], + Sysmgr_Emac_Phy_Intf_Sel_E_Rgmii[instance]); + + /* 4b. Disable the Ethernet Controller FPGA interfaces by clearing the + * emac_* bit in the module register of the System Manager (FPGA Interface + * group). + */ + + sys_clear_bits(SYSMGR_FPGAINTF_INDIV_ADDR, Sysmgr_Fpgaintf_En_3_Emac_Set_Msk[instance]); + + /* 7. After confirming the settings are valid, software can clear the emac + * bit in the permodrst register of the Reset Manager to bring the EMAC out of + * reset. + */ + + sys_clear_bits(RSTMGR_PERMODRST_ADDR, Rstmgr_Permodrst_Emac_Set_Msk[instance]); +} + +/** + * @brief Set MAC Address function + * Loads the selected MAC Address in device's registers. + * + * @param address Pointer to Mac Address table + * @param instance Number of instance (0 or 1 in Cyclone V HPS) + * @param n Selected index of MAC Address, n <= 15. There's no implementation + * of setting MAC Addresses for n > 15. + * + */ + +void eth_cyclonev_set_mac_addr(uint8_t *address, uint32_t instance, uint32_t n, + struct eth_cyclonev_priv *p) +{ + uint32_t tmpreg; + + if (instance > 1) { + return; + } + if (n > 15) { + LOG_ERR("Invalid index of MAC Address: %d", n); + return; + } + + /* Calculate the selected MAC address high register */ + tmpreg = ((uint32_t)address[5] << 8) | (uint32_t)address[4]; + + /* Load the selected MAC address high register */ + sys_write32(tmpreg, EMAC_GMAC_MAC_ADDR_HIGH_ADDR(p->base_addr, n)); + + /* Calculate the selected MAC address low register */ + tmpreg = ((uint32_t)address[3] << 24) | ((uint32_t)address[2] << 16) | + ((uint32_t)address[1] << 8) | address[0]; + + /* Load the selected MAC address low register */ + sys_write32(tmpreg, EMAC_GMAC_MAC_ADDR_LOW_ADDR(p->base_addr, n)); +} + +/** + * @brief Get software reset status function + * Check status of SWR bit in DMA Controller Bus_Mode Register + * + * @param instance Number of instance (0 or 1 in Cyclone V HPS) + * @retval 1 if DMA Controller Resets Logic, 0 otherwise + */ + +int eth_cyclonev_get_software_reset_status(uint32_t instance, struct eth_cyclonev_priv *p) +{ + if (instance > 1) { + return -1; + } + return EMAC_DMA_MODE_SWR_GET(sys_read32(EMAC_DMAGRP_BUS_MODE_ADDR(p->base_addr))); +} + +/** + * @brief Perform software reset + * Resets all MAC subsystem registers and logic, wait for the software reset to + * clear + * + * @param instance Number of instance (0 or 1 in Cyclone V HPS) + * @retval 0 if Reset was successful, -1 otherwise + */ + +int eth_cyclonev_software_reset(uint32_t instance, struct eth_cyclonev_priv *p) +{ + unsigned int i; + + if (instance > 1) { + return -1; + } + + /* Set the SWR bit: resets all MAC subsystem internal registers and logic */ + /* After reset all the registers holds their respective reset values */ + sys_set_bits(EMAC_DMAGRP_BUS_MODE_ADDR(p->base_addr), EMAC_DMA_MODE_SWR_SET_MSK); + + /* Wait for the software reset to clear */ + for (i = 0; i < 10; i++) { + k_sleep(K_MSEC(10)); + if (eth_cyclonev_get_software_reset_status(instance, p) == 0) { + break; + } + } + + if (i == 10) { + return -1; + } + + return 0; +} + +/** + * @brief RX descriptor ring initialisation function + * Sets up RX descriptor ring with chained descriptors, + * sets OWN bit in each descriptor, inits rx variables and stats + * + * @param p Pointer to device structure. + */ + +void eth_cyclonev_setup_rxdesc(struct eth_cyclonev_priv *p) +{ + int32_t i; + struct eth_cyclonev_dma_desc *rx_desc; + + /* For each descriptor where i = descriptor index do: */ + for (i = 0; i < NB_RX_DESCS; i++) { + rx_desc = &p->rx_desc_ring[i]; + rx_desc->buffer1_addr = (uint32_t)&p->rx_buf[i * ETH_BUFFER_SIZE]; + rx_desc->control_buffer_size = ETH_DMARXDESC_RCH | ETH_BUFFER_SIZE; + + /*set own bit*/ + rx_desc->status = ETH_DMARXDESC_OWN; + + rx_desc->buffer2_next_desc_addr = (uint32_t)&p->rx_desc_ring[i + 1]; + if (i == (NB_RX_DESCS - 1)) { + rx_desc->buffer2_next_desc_addr = (uint32_t)&p->rx_desc_ring[0]; + } + } + + p->rx_current_desc_number = 0; + p->rxints = 0; + + /* Set RX Descriptor List Address Register */ + sys_write32((uint32_t)&p->rx_desc_ring[0], + EMAC_DMA_RX_DESC_LIST_ADDR(p->base_addr)); +} + +/** + * @brief TX descriptor ring initialisation function + * Sets up TX descriptor ring with chained descriptors, + * sets OWN bit in each descriptor, inits rx variables and stats + * + * @param p Pointer to device structure. + */ + +void eth_cyclonev_setup_txdesc(struct eth_cyclonev_priv *p) +{ + int32_t i; + + struct eth_cyclonev_dma_desc *tx_desc; + + /* For each descriptor where i = descriptor index do: */ + for (i = 0; i < NB_TX_DESCS; i++) { + tx_desc = &p->tx_desc_ring[i]; + tx_desc->buffer1_addr = (uint32_t)&p->tx_buf[i * ETH_BUFFER_SIZE]; + tx_desc->buffer2_next_desc_addr = (uint32_t)&p->tx_desc_ring[i + 1]; + tx_desc->status = 0; + tx_desc->control_buffer_size = 0; + + if (i == (NB_TX_DESCS - 1)) { + tx_desc->buffer2_next_desc_addr = (uint32_t)&p->tx_desc_ring[0]; + } + } + + p->tx_current_desc_number = 0; + p->txints = 0; + p->tx_tail = 0; + + /* Set TX Descriptor List Address Register */ + sys_write32((uint32_t)&p->tx_desc_ring[0], + EMAC_DMA_TX_DESC_LIST_ADDR(p->base_addr)); +} + +/** + * @brief Ethernet interface initialisation function + * Inits interface, sets interface link MAC address + * + * @param iface Pointer to net_if structure + */ + +/* Initialisation of interface */ +static void eth_cyclonev_iface_init(struct net_if *iface) +{ + const struct device *dev = net_if_get_device(iface); + const struct eth_cyclonev_config *config = dev->config; + struct eth_cyclonev_priv *p = dev->data; + + p->iface = iface; + ethernet_init(iface); + net_if_set_link_addr(iface, p->mac_addr, sizeof(p->mac_addr), NET_LINK_ETHERNET); + + /* + * Semaphores are used to represent number of available descriptors. + * The total is one less than ring size in order to always have + * at least one inactive slot for the hardware tail pointer to + * stop at and to prevent our head indexes from looping back + * onto our tail indexes. + */ + k_sem_init(&p->free_tx_descs, NB_TX_DESCS - 1, NB_TX_DESCS - 1); + + /* Initialize the ethernet irq handler */ + config->irq_config(); + + p->initialised = 1; + LOG_DBG("done"); +} + +/** + * @brief Ethernet set config function usually called by + * Zephyr Ethernet stack. It supports currently two options: + * Set of Mac address and Enabling Promiscuous Mode + * + * @param dev Pointer to net_if structure + * @param type Enumerated type of configuration to do + * @param config Pointer to ethernet_config structure + * @retval ret 0 if successful + */ + +static int eth_cyclonev_set_config(const struct device *dev, enum ethernet_config_type type, + const struct ethernet_config *config) +{ + struct eth_cyclonev_priv *p = dev->data; + uint32_t reg_val; + int ret = 0; + + (void)reg_val; /* silence the "unused variable" warning */ + + switch (type) { + case ETHERNET_CONFIG_TYPE_MAC_ADDRESS: + memcpy(p->mac_addr, config->mac_address.addr, sizeof(p->mac_addr)); + eth_cyclonev_set_mac_addr(p->mac_addr, p->instance, 0, p); /* Set MAC address */ + net_if_set_link_addr(p->iface, p->mac_addr, sizeof(p->mac_addr), NET_LINK_ETHERNET); + break; +#if defined(CONFIG_NET_PROMISCUOUS_MODE) + case ETHERNET_CONFIG_TYPE_PROMISC_MODE: + reg_val = sys_read32(EMAC_GMACGRP_MAC_FRAME_FILTER_ADDR(p->base_addr)); + if (config->promisc_mode && !(reg_val & EMAC_GMACGRP_MAC_FRAME_FILTER_PR_SET_MSK)) { + /* Turn on Promisc Mode */ + sys_set_bits(EMAC_GMACGRP_MAC_FRAME_FILTER_ADDR(p->base_addr), + EMAC_GMACGRP_MAC_FRAME_FILTER_PR_SET_MSK); + } else if (!config->promisc_mode && + (reg_val & EMAC_GMACGRP_MAC_FRAME_FILTER_PR_SET_MSK)) { + /* Turn off Promisc Mode */ + sys_clear_bits(EMAC_GMACGRP_MAC_FRAME_FILTER_ADDR(p->base_addr), + EMAC_GMACGRP_MAC_FRAME_FILTER_PR_SET_MSK); + } else { + ret = -EALREADY; + } + break; +#endif + default: + ret = -ENOTSUP; + break; + } + LOG_DBG("set_config: ret = %d ", ret); + return ret; +} + +/** + * @brief Get capabilities function usually called by + * Zephyr Ethernet stack. + * + * @param dev Pointer to net_if structure + * @retval caps Enumerated capabilities of device + */ + +static enum ethernet_hw_caps eth_cyclonev_caps(const struct device *dev) +{ + struct eth_cyclonev_priv *p = dev->data; + enum ethernet_hw_caps caps = 0; + + if (p->feature & EMAC_DMA_HW_FEATURE_MIISEL) { + caps |= ETHERNET_LINK_10BASE_T; + caps |= ETHERNET_LINK_100BASE_T; + } + if (p->feature & EMAC_DMA_HW_FEATURE_GMIISEL) { + caps |= ETHERNET_LINK_1000BASE_T; + } + if (p->feature & EMAC_DMA_HW_FEATURE_RXTYP2COE) { + caps |= ETHERNET_HW_RX_CHKSUM_OFFLOAD; + } + if (p->feature & EMAC_DMA_HW_FEATURE_RXTYP1COE) { + caps |= ETHERNET_HW_RX_CHKSUM_OFFLOAD; + } + + caps |= ETHERNET_PROMISC_MODE; + + return caps; +} + +/** + * @brief Send packet function + * Sends packet of data. See: + * https://www.intel.com/content/dam/www/programmable/us/en/pdfs/ + * literature/hb/cyclone-v/cv_54001.pdf p.1254 and p.1206 + * + * @param dev Pointer to device structure + * @param pkt Pointer to net_pkt structure containing packet to sent + * @retval 0 if successful, -1 otherwise + */ + +static int eth_cyclonev_send(const struct device *dev, struct net_pkt *pkt) +{ + LOG_DBG("ethernet CVSX sending...\n"); + + struct eth_cyclonev_priv *p = dev->data; + struct eth_cyclonev_dma_desc *tx_desc; + int32_t index = 0; + uint16_t len = net_pkt_get_len(pkt); + int first = 1; + struct net_buf *frag; + + LOG_DBG("Pkt length: %d", len); + frag = pkt->buffer; + do { + + /* reserve a free descriptor for this fragment */ + if (k_sem_take(&p->free_tx_descs, TX_AVAIL_WAIT) != 0) { + LOG_DBG("no more free tx descriptors"); + goto abort; + } + + tx_desc = &p->tx_desc_ring[p->tx_current_desc_number]; + + /* Check if it is a free descriptor. */ + if (tx_desc->status & ETH_DMATXDESC_OWN) { + /* Buffer is still owned by device. */ + LOG_ERR("No free tx descriptors!\n"); + goto abort; + } + + /* check if len is too large */ + if (len >= ETH_BUFFER_SIZE) { + LOG_ERR("Length of packet is too long\n"); + goto abort; + } + + /* Copy data to local buffer */ + + if (frag) { + memcpy(&p->tx_buf[p->tx_current_desc_number * ETH_BUFFER_SIZE], frag->data, + len); + } + + /* Set the buffer size. */ + tx_desc->control_buffer_size = (frag->len & ETH_DMATXDESC_TBS1); + + LOG_DBG("Desc[%d] at address: 0x%08x: , Frag size: %d, Buffer Addr: %p", + p->tx_current_desc_number, + (unsigned int)&p->tx_desc_ring[p->tx_current_desc_number], frag->len, + (void *)tx_desc->buffer1_addr); + + tx_desc->status = ETH_DMATXDESC_TCH; + + /* Set the Descriptor's FS bit. */ + if (first) { + tx_desc->status |= (ETH_DMATXDESC_FS | ETH_DMATXDESC_CIC_BYPASS); + first = 0; + } + + /* If Last: then (...) */ + if (!frag->frags) { + /* set the Descriptor's LS and IC bit. */ + tx_desc->status |= (ETH_DMATXDESC_LS | ETH_DMATXDESC_IC); + index = p->tx_current_desc_number; + } + + /* Set the current index to the next descriptor. */ + p->tx_current_desc_number = (p->tx_current_desc_number + 1); + if (p->tx_current_desc_number >= NB_TX_DESCS) { + p->tx_current_desc_number = 0; + } + + if (!frag->frags) { + + while (1) { + + tx_desc = &p->tx_desc_ring[index]; + + if (tx_desc->status & ETH_DMATXDESC_OWN) { + LOG_ERR("Send packet error!\n"); + /* Restart DMA transmission and re-initialise + * TX descriptors + */ + sys_clear_bits(EMAC_DMAGRP_OPERATION_MODE_ADDR( + p->base_addr), + EMAC_DMAGRP_OPERATION_MODE_ST_SET_MSK); + sys_set_bits(EMAC_DMAGRP_OPERATION_MODE_ADDR( + p->base_addr), + EMAC_DMAGRP_OPERATION_MODE_FTF_SET_MSK); + eth_cyclonev_setup_txdesc(p); + sys_set_bits(EMAC_DMAGRP_OPERATION_MODE_ADDR( + p->base_addr), + EMAC_DMAGRP_OPERATION_MODE_ST_SET_MSK); + goto abort; + } + + /* Set OWN bit. */ + tx_desc->status |= ETH_DMATXDESC_OWN; + + if (tx_desc->status & ETH_DMATXDESC_FS) { + break; + } + + index--; + if (index < 0) { + index = NB_TX_DESCS - 1; + } + } + + LOG_DBG("Current Host Transmit Descriptor Register: 0x%08x", + sys_read32( + EMAC_DMA_CURR_HOST_TX_DESC_ADDR(p->base_addr))); + LOG_DBG("Current Host Transmit Buffer Register: 0x%08x", + sys_read32( + EMAC_DMA_CURR_HOST_TX_BUFF_ADDR(p->base_addr))); + + /* If the DMA transmission is suspended, resume transmission. */ + if (sys_read32(EMAC_DMAGRP_STATUS_ADDR(p->base_addr)) & + EMAC_DMAGRP_STATUS_TS_SET_MSK) { + + /* Clear TBUS ETHERNET DMA flag */ + sys_write32(EMAC_DMAGRP_STATUS_TS_SET_MSK, + EMAC_DMAGRP_STATUS_ADDR(p->base_addr)); + + /* Resume DMA transmission */ + sys_write32(0, + EMAC_DMA_TX_POLL_DEMAND_ADDR(p->base_addr)); + } + } + frag = frag->frags; + } while (frag); + + LOG_DBG("Sent"); + return 0; + +abort: + k_sem_give(&p->free_tx_descs); /* Multi-descriptor package release (?) */ + + return -1; +} + +/** + * @brief Interrupt handling function + * Detects interrupt status, invokes necessary actions + * and clears interrupt status register + * + * @param dev Pointer to device structure + */ + +void eth_cyclonev_isr(const struct device *dev) +{ + struct eth_cyclonev_priv *p = dev->data; + uint32_t irq_status = 0; + uint32_t irq_status_emac = 0; + + irq_status = + sys_read32(EMAC_DMAGRP_STATUS_ADDR(p->base_addr)) & p->interrupt_mask; + irq_status_emac = sys_read32(EMAC_GMAC_INT_STAT_ADDR(p->base_addr)); + LOG_DBG("DMA_IRQ_STATUS = 0x%08x, emac: 0x%08x", irq_status, irq_status_emac); + + if (irq_status & EMAC_DMA_INT_EN_NIE_SET_MSK) { + sys_write32(EMAC_DMA_INT_EN_NIE_SET_MSK, + EMAC_DMAGRP_STATUS_ADDR(p->base_addr)); + } + + if (irq_status & EMAC_DMA_INT_EN_TIE_SET_MSK) { + p->txints++; + eth_cyclonev_tx_release(p); + /* Clear the selected ETHERNET DMA bit(s) */ + sys_write32(EMAC_DMA_INT_EN_TIE_SET_MSK, + EMAC_DMAGRP_STATUS_ADDR(p->base_addr)); + } + + if (irq_status & EMAC_DMA_INT_EN_RIE_SET_MSK) { + p->rxints++; + eth_cyclonev_receive(p); + /* Clear the selected ETHERNET DMA bit(s) */ + sys_write32(EMAC_DMA_INT_EN_RIE_SET_MSK, + EMAC_DMAGRP_STATUS_ADDR(p->base_addr)); + } + + if (irq_status_emac & EMAC_GMAC_INT_STAT_RGSMIIIS_SET_MSK) { + /* Clear the selected ETHERNET GMAC bit(s) */ + uint32_t regval = sys_read32(GMACGRP_CONTROL_STATUS_ADDR(p->base_addr)); + + if (EMAC_GMAC_MII_CTL_STAT_LNKSTS_GET(regval)) { + LOG_INF("Link is up"); + } else { + LOG_INF("Link is down"); + return; + } + + if (EMAC_GMAC_MII_CTL_STAT_LNKMOD_GET(regval)) { + LOG_INF("Full duplex"); + } else { + LOG_INF("Half duplex"); + } + + switch (EMAC_GMAC_MII_CTL_STAT_LNKSPEED_GET(regval)) { + case 0: + LOG_INF("Link Speed 2.5MHz"); + break; + case 1: + LOG_INF("Link Speed 25MHz"); + break; + case 2: + LOG_INF("Link Speed 125MHz"); + break; + default: + LOG_ERR("LNKSPEED_GET_ERROR"); + break; + } + + if (p->initialised) { + uint32_t cfg_reg_set; + + cfg_reg_set = sys_read32(GMACGRP_MAC_CONFIG_ADDR(p->base_addr)); + + if (eth_cyclonev_stop(dev) == -1) { + LOG_ERR("Couldn't stop device: %s", dev->name); + return; + } + + set_mac_conf_status(p->instance, &cfg_reg_set, p); + sys_write32(cfg_reg_set, GMACGRP_MAC_CONFIG_ADDR(p->base_addr)); + + eth_cyclonev_start(dev); + } + } +} + +/** + * @brief Receive packet function (IRQ) + * In the event of receive completion interrupt, this function + * copies data from buffer to necessary net stack structures + * performs general error checking and returns descriptor to hardware. + * + * @param p Pointer to device structure + * + */ + +static void eth_cyclonev_receive(struct eth_cyclonev_priv *p) +{ + struct eth_cyclonev_dma_desc *rx_desc; + struct net_pkt *pkt; + uint32_t index, frame_length, rx_search, wrap, data_remaining, last_desc_index, buf_size; + + index = p->rx_current_desc_number; + rx_desc = &p->rx_desc_ring[index]; + + while (!(rx_desc->status & ETH_DMARXDESC_OWN)) { + + LOG_DBG("RDES0[%d] = 0x%08x", index, rx_desc->status); + /* Look for FS bit */ + if (!(rx_desc->status & ETH_DMARXDESC_FS)) { + LOG_ERR("Unexpected missing FS bit"); + rx_desc->status |= ETH_DMARXDESC_OWN; + goto cont; + } + /* Look for EOF bit, save frame length including multiple + * buffers and index of last descriptor + */ + rx_search = index; + wrap = index; + do { + rx_desc = &p->rx_desc_ring[rx_search]; + /* Frame length */ + frame_length = data_remaining = (ETH_DMARXDESC_FL & rx_desc->status) >> 16; + last_desc_index = rx_search; + if (!(rx_desc->status & ETH_DMARXDESC_LS)) { + INC_WRAP(rx_search, NB_RX_DESCS); + if (rx_search == wrap) { + LOG_ERR("Couldn't find EOF bit!"); + rx_desc = &p->rx_desc_ring[index]; + rx_desc->status |= ETH_DMARXDESC_OWN; + goto cont; + } + } + } while (!(rx_desc->status & ETH_DMARXDESC_LS)); + + LOG_DBG("Frame length = %d, Last descriptor = %d", frame_length, last_desc_index); + p->rx_current_desc_number = last_desc_index; + + /* Allocate packet with buffer */ + pkt = net_pkt_rx_alloc_with_buffer(p->iface, frame_length, AF_UNSPEC, 0, K_NO_WAIT); + if (!pkt) { + LOG_ERR("net_pkt_rx_alloc_with_buffer() failed"); + eth_stats_update_errors_rx(p->iface); + } + + /* Copy data from multiple buffers and descriptors */ + rx_search = index; + wrap = index; + do { + rx_desc = &p->rx_desc_ring[rx_search]; + if (data_remaining < ETH_BUFFER_SIZE) { + buf_size = data_remaining; + } else { + buf_size = ETH_BUFFER_SIZE; + } + if (pkt) { + net_pkt_write(pkt, &p->rx_buf[rx_search * ETH_BUFFER_SIZE], + buf_size); + } + data_remaining -= buf_size; + rx_desc->status |= ETH_DMARXDESC_OWN; + if (last_desc_index != rx_search) { + INC_WRAP(rx_search, NB_RX_DESCS); + if (rx_search == wrap) { + LOG_ERR("Couldn't find last descriptor! Data remaining: %d", + data_remaining); + goto cont; + } + if (rx_search == last_desc_index) { + /* One more iteration */ + rx_desc = &p->rx_desc_ring[rx_search]; + if (data_remaining < ETH_BUFFER_SIZE) { + buf_size = data_remaining; + } else { + buf_size = ETH_BUFFER_SIZE; + } + if (pkt) { + net_pkt_write( + pkt, + &p->rx_buf[rx_search * ETH_BUFFER_SIZE], + buf_size); + } + data_remaining -= buf_size; + + rx_desc->status |= ETH_DMARXDESC_OWN; + } + } + } while (last_desc_index != rx_search); + + /* Hand-over packet into IP stack */ + if (pkt) { + if (net_recv_data(p->iface, pkt) < 0) { + LOG_ERR("RX packet hand-over to IP stack failed"); + net_pkt_unref(pkt); + } + LOG_DBG("Received packet %p, len %d", pkt, frame_length); + } + +cont: + p->rx_current_desc_number++; + if (p->rx_current_desc_number == NB_RX_DESCS) { + p->rx_current_desc_number = 0; + } + index = p->rx_current_desc_number; + rx_desc = &p->rx_desc_ring[index]; + } +} + +/** + * @brief Release tx function + * Main purpose of its function is to track current descriptor number + * and give back succeding tx semaphore when it have been used. + * + * @param p Pointer to device structure + */ + +static void eth_cyclonev_tx_release(struct eth_cyclonev_priv *p) +{ + unsigned int d_idx; + struct eth_cyclonev_dma_desc *d; + uint32_t des3_val; + + for (d_idx = p->tx_tail; d_idx != p->tx_current_desc_number; + INC_WRAP(d_idx, NB_TX_DESCS), k_sem_give(&p->free_tx_descs)) { + + d = &p->tx_desc_ring[d_idx]; + des3_val = d->status; + LOG_DBG("TDES3[%d] = 0x%08x", d_idx, des3_val); + + /* stop here if hardware still owns it */ + if (des3_val & ETH_DMATXDESC_OWN) { + break; + } + + /* last packet descriptor: */ + if (des3_val & ETH_DMATXDESC_LS) { + /* log any errors */ + if (des3_val & ETH_DMATXDESC_ES) { + LOG_ERR("tx error (DES3 = 0x%08x)", des3_val); + eth_stats_update_errors_tx(p->iface); + } + } + } + p->tx_tail = d_idx; +} + +/** + * @brief Sets MAC Config Register (not implemented) + * Detects PHY Mode and assigns MAC Config Register + * + * @param instance Number of instance (0 or 1 in Cyclone V HPS) + * @param mac_config_reg_settings Mac_config register mask to set + * @retval updated mac_config_reg mask (>=0), -1 otherwise + */ +/* Configure the MAC with the speed fixed by the auto-negotiation process */ +int set_mac_conf_status(int instance, uint32_t *mac_config_reg_settings, + struct eth_cyclonev_priv *p) +{ + uint16_t phy_duplex_status, phy_speed; + int ret; + + ret = alt_eth_phy_get_duplex_and_speed(&phy_duplex_status, &phy_speed, instance, p); + if (ret != 0) { + LOG_ERR("alt_eth_phy_get_duplex_and_speed failure!"); + return ret; + } + + /* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */ + if (phy_duplex_status != 0) { + *mac_config_reg_settings |= EMAC_GMACGRP_MAC_CONFIGURATION_DM_SET_MSK; + } + /* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */ + else { + *mac_config_reg_settings &= ~EMAC_GMACGRP_MAC_CONFIGURATION_DM_SET_MSK; + } + + /* Set Ethernet speed to 10M following the auto-negotiation */ + if (phy_speed == 10) { + *mac_config_reg_settings &= ~EMAC_GMACGRP_MAC_CONFIGURATION_FES_SET_MSK; + *mac_config_reg_settings |= EMAC_GMACGRP_MAC_CONFIGURATION_PS_SET_MSK; + } + + /* Set Ethernet speed to 100M following the auto-negotiation */ + if (phy_speed == 100) { + *mac_config_reg_settings |= EMAC_GMACGRP_MAC_CONFIGURATION_FES_SET_MSK; + *mac_config_reg_settings |= EMAC_GMACGRP_MAC_CONFIGURATION_PS_SET_MSK; + } + + /* Set Ethernet speed to 1G following the auto-negotiation */ + if (phy_speed == 1000) { + *mac_config_reg_settings &= ~EMAC_GMACGRP_MAC_CONFIGURATION_PS_SET_MSK; + } + + return 0; +} + +/** + * @brief Hardware initialisation function + * Performs EMAC HPS interface initialisation, DMA initialisation, + * EMAC initialisation and configuration. See: + * https://www.intel.com/content/dam/ + * www/programmable/us/en/pdfs/literature/hb/cyclone-v/cv_54001.pdf p.1252-54 + * + * @param dev Pointer to device structure + * @retval 0 if successful, -1 otherwise + */ + +int eth_cyclonev_probe(const struct device *dev) +{ + struct eth_cyclonev_priv *p = dev->data; + const struct eth_cyclonev_config *config = dev->config; + uint32_t tmpreg = 0, interrupt_mask; + uint32_t mac_config_reg_settings = 0; + int ret; + + p->base_addr = (mem_addr_t)config->base; + p->running = 0; + p->initialised = 0; + p->instance = config->inst_num; + + /* EMAC HPS Interface Initialization */ + + /* Reset the EMAC */ + eth_cyclonev_reset(p->instance); + + /* Reset the PHY */ + ret = alt_eth_phy_reset(p->instance, p); + if (ret != 0) { + LOG_ERR("alt_eth_phy_reset failure!\n"); + return ret; + } + + /* Configure the PHY */ + ret = alt_eth_phy_config(p->instance, p); + if (ret != 0) { + LOG_ERR("alt_eth_phy_config failure!\n"); + return ret; + } + + /* Read HW feature register */ + + p->feature = sys_read32(EMAC_DMA_HW_FEATURE_ADDR(p->base_addr)); + + /* DMA Initialisation */ + + /* 1. Provide a software reset to reset all of the EMAC internal registers and + *logic. (DMA Register 0 (BusMode Register) – bit 0). + * 2. Wait for the completion of the reset process (poll bit 0 of the DMA + *Register 0 (Bus Mode Register), which is only cleared after the reset + *operation is completed). + */ + + ret = eth_cyclonev_software_reset(p->instance, p); + if (ret != 0) { + LOG_ERR("eth_cyclonev_software_reset failure!\n"); + return ret; + } + + /* 4. Program the following fields to initialize the Bus Mode Register by + * setting values in DMA Register 0 (Bus Mode Register): + */ + + sys_write32((tmpreg | EMAC_DMA_MODE_FB_SET_MSK /* Fixed Burst */ + ), + EMAC_DMAGRP_BUS_MODE_ADDR(p->base_addr)); + + /* 5. Program the interface options in Register 10 (AXI Bus Mode + * Register). If fixed burst-length is enabled, then select the maximum + * burst-length possible on the bus (bits[7:1]).(58) + */ + + tmpreg = sys_read32(EMAC_DMAGRP_AXI_BUS_MODE_ADDR(p->base_addr)); + + sys_write32( + tmpreg | EMAC_DMAGRP_AXI_BUS_MODE_BLEN16_SET_MSK, + EMAC_DMAGRP_AXI_BUS_MODE_ADDR(p->base_addr)); /* Set Burst Length = 16 */ + + /* 6. Create a proper descriptor chain for transmit and receive. In addition, + * ensure that the receive descriptors are owned by DMA (bit 31 of descriptor + * should be set). + * 7. Make sure that your software creates three or more different transmit or + * receive descriptors in the chain before reusing any of the descriptors + * 8. Initialize receive and transmit descriptor list address with the base + * address of the transmit and receive descriptor (Register 3 (Receive + * Descriptor List Address Register) and Register 4 (Transmit Descriptor List + * Address Register) respectively). + */ + + eth_cyclonev_setup_rxdesc(p); + eth_cyclonev_setup_txdesc(p); + + /* 9. Program the following fields to initialize the mode of operation in + * Register 6 (Operation Mode Register): + */ + + sys_write32((0 | EMAC_DMAGRP_OPERATION_MODE_TSF_SET_MSK /* Transmit Store and Forward */ + | EMAC_DMAGRP_OPERATION_MODE_RSF_SET_MSK /* Receive Store and Forward */ + | EMAC_DMAGRP_OPERATION_MODE_FTF_SET_MSK /* Receive Store and Forward */ + ), + EMAC_DMAGRP_OPERATION_MODE_ADDR(p->base_addr)); + + /* 10.Clear the interrupt requests, by writing to those bits of the status + * register (interrupt bits only) that are set. For example, by writing 1 into + * bit 16, the normal interrupt summary clears this bit (DMA Register 5 + * (Status Register)). + */ + + interrupt_mask = EMAC_DMA_INT_EN_NIE_SET_MSK | EMAC_DMA_INT_EN_RIE_SET_MSK | + EMAC_DMA_INT_EN_TIE_SET_MSK; + + p->interrupt_mask = interrupt_mask; + + /* Clear the selected ETHERNET DMA bit(s) */ + sys_write32(interrupt_mask, EMAC_DMAGRP_STATUS_ADDR(p->base_addr)); + + /* 11.Enable the interrupts by programming Register 7 (Interrupt Enable + * Register). + */ + + sys_set_bits(EMAC_DMA_INT_EN_ADDR(p->base_addr), interrupt_mask); + + /* 12.Read Register 11 (AHB or AXI Status) to confirm that + * all previous transactions are complete. + */ + + if (sys_read32(EMAC_DMAGRP_AHB_OR_AXI_STATUS_ADDR(p->base_addr)) != 0) { + LOG_ERR("AHB_OR_AXI_STATUS Fail!\n"); + return -1; + } + + /* EMAC Initialization and Configuration */ + + /* 1. Program the GMII Address Register (offset 0x10) for controlling the + * management cycles for theexternal PHY. Bits[15:11] of the GMII Address + * Register are written with the Physical Layer Address of the PHY before + * reading or writing. Bit 0 indicates if the PHY is busy and is set before + * reading or writing to the PHY management interface. + * 2. Read the 16-bit data of the GMII Data Register from the PHY for link up, + * speed of operation, and mode of operation, by specifying the appropriate + * address value in bits[15:11] of the GMII Address Register. + */ + + mac_config_reg_settings = (EMAC_GMACGRP_MAC_CONFIGURATION_IPC_SET_MSK + /* Checksum Offload */ + | EMAC_GMACGRP_MAC_CONFIGURATION_JD_SET_MSK + /* Jabber Disable */ + | EMAC_GMACGRP_MAC_CONFIGURATION_BE_SET_MSK + /* Frame Burst Enable */ + | EMAC_GMACGRP_MAC_CONFIGURATION_WD_SET_MSK + /* Watchdog Disable */ + | EMAC_GMACGRP_MAC_CONFIGURATION_TC_SET_MSK + /* Enable Transmission to PHY */ + ); + + ret = set_mac_conf_status(p->instance, &mac_config_reg_settings, p); + if (ret != 0) { + return -1; + } + + /* 3. Provide the MAC address registers (MAC Address0 High Register + * through MAC Address15 High Register and MAC Address0 Low Register + * through MAC Address15 Low Register). + */ + + memcpy(p->mac_addr, eth_cyclonev_mac_addr, sizeof(p->mac_addr)); + eth_cyclonev_set_mac_addr(p->mac_addr, p->instance, 0, p); + + /* 5. Program the following fields to set the appropriate filters for the + * incoming frames in the MAC Frame Filter Register: + * • Receive All + * • Promiscuous mode + * • Hash or Perfect Filter + * • Unicast, multicast, broadcast, and control frames filter settings + */ + + sys_clear_bits(EMAC_GMACGRP_MAC_FRAME_FILTER_ADDR(p->base_addr), + EMAC_GMACGRP_MAC_FRAME_FILTER_PR_SET_MSK); /* Disable promiscuous mode */ + + /* 7. Program the Interrupt Mask Register bits, + * as required and if applicable for your configuration. + */ + + sys_set_bits(EMAC_GMAC_INT_MSK_ADDR(p->base_addr), + EMAC_GMAC_INT_STAT_LPIIS_SET_MSK | /* Disable Low Power IRQ */ + EMAC_GMAC_INT_STAT_TSIS_SET_MSK); /* Disable Timestamp IRQ */ + + /* 8. Program the appropriate fields in MAC Configuration Register to + * configure receive and transmit operation modes... + */ + + sys_write32(mac_config_reg_settings, GMACGRP_MAC_CONFIG_ADDR(p->base_addr)); + + LOG_DBG("func_eth_cyclonev_probe Success!\n"); + return 0; +} + +/** + * @brief Start device function + * Starts DMA and EMAC transmitter and receiver. See: + * https://www.intel.com/content/dam/ + * www/programmable/us/en/pdfs/literature/hb/cyclone-v/cv_54001.pdf p.1255-56 + * + * @param dev Pointer to device structure + * @retval 0 + */ + +static int eth_cyclonev_start(const struct device *dev) +{ + + struct eth_cyclonev_priv *p = dev->data; + + if (p->running) { + LOG_DBG("Device already running!"); + return 0; + } + + /*6. To re-start the operation, first start the DMA and then enable + * the EMAC transmitter and receiver. + */ + + /* Start the DMA */ + sys_set_bits(EMAC_DMAGRP_OPERATION_MODE_ADDR(p->base_addr), + EMAC_DMAGRP_OPERATION_MODE_ST_SET_MSK); + sys_set_bits(EMAC_DMAGRP_OPERATION_MODE_ADDR(p->base_addr), + EMAC_DMAGRP_OPERATION_MODE_SR_SET_MSK); + + /* Enable the EMAC transmitter and receiver */ + sys_set_bits(GMACGRP_MAC_CONFIG_ADDR(p->base_addr), + EMAC_GMACGRP_MAC_CONFIGURATION_TE_SET_MSK); + sys_set_bits(EMAC_DMAGRP_OPERATION_MODE_ADDR(p->base_addr), + EMAC_DMAGRP_OPERATION_MODE_FTF_SET_MSK); /* Flush Transmit FIFO */ + sys_set_bits(GMACGRP_MAC_CONFIG_ADDR(p->base_addr), + EMAC_GMACGRP_MAC_CONFIGURATION_RE_SET_MSK); + + p->running = 1; + LOG_DBG("Starting Device..."); + return 0; +} + +/** + * @brief Stop device function + * Stops DMA and EMAC transmitter and receiver. See: + * https://www.intel.com/content/dam/www/ + * programmable/us/en/pdfs/literature/hb/cyclone-v/cv_54001.pdf p.1255-56 + * + * @param dev Pointer to device structure + * @retval 0 if successful, -1 otherwise + */ + +static int eth_cyclonev_stop(const struct device *dev) +{ + + struct eth_cyclonev_priv *p = dev->data; + + if (!p->running) { + LOG_DBG("Device is not running!"); + return 0; + } + /* 1. Disable the transmit DMA (if applicable), by clearing bit 13 + * (Start or Stop Transmission Command) of Register 6 (Operation Mode + * Register). + */ + + sys_clear_bits(EMAC_DMAGRP_OPERATION_MODE_ADDR(p->base_addr), + EMAC_DMAGRP_OPERATION_MODE_ST_SET_MSK); + + /* 3. Disable the EMAC transmitter and EMAC receiver by clearing Bit 3 + * (TE) and Bit 2 (RE) in Register 0 (MAC Configuration Register). + */ + sys_clear_bits(GMACGRP_MAC_CONFIG_ADDR(p->base_addr), + EMAC_GMACGRP_MAC_CONFIGURATION_TE_SET_MSK); + sys_set_bits(EMAC_DMAGRP_OPERATION_MODE_ADDR(p->base_addr), + EMAC_DMAGRP_OPERATION_MODE_FTF_SET_MSK); /* Flush Transmit FIFO */ + sys_clear_bits(GMACGRP_MAC_CONFIG_ADDR(p->base_addr), + EMAC_GMACGRP_MAC_CONFIGURATION_RE_SET_MSK); + + /* 4. Disable the receive DMA (if applicable), after making sure that the data + * in the RX FIFO buffer is transferred to the system memory + * (by reading Register 9 (Debug Register). + */ + + sys_clear_bits(EMAC_DMAGRP_OPERATION_MODE_ADDR(p->base_addr), + EMAC_DMAGRP_OPERATION_MODE_SR_SET_MSK); + + /* 5. Make sure that both the TX FIFO buffer and RX FIFO buffer are empty. */ + + if (EMAC_DMAGRP_DEBUG_RXFSTS_GET( + sys_read32(EMAC_DMAGRP_DEBUG_ADDR(p->base_addr))) != 0x0) { + return -1; + } + + p->running = 0; + LOG_DBG("Stopping Device..."); + return 0; +} + +const struct ethernet_api eth_cyclonev_api = {.iface_api.init = eth_cyclonev_iface_init, + .get_capabilities = eth_cyclonev_caps, + .send = eth_cyclonev_send, + .start = eth_cyclonev_start, + .stop = eth_cyclonev_stop, + .set_config = eth_cyclonev_set_config}; + +#define CYCLONEV_ETH_INIT(inst) \ + \ + static struct eth_cyclonev_priv eth_cyclonev_##inst##_data; \ + static void eth_cyclonev_##inst##_irq_config(void); \ + \ + static const struct eth_cyclonev_config eth_cyclonev_##inst##_cfg = { \ + .base = (uint8_t *)(DT_INST_REG_ADDR(inst)), \ + .size = DT_INST_REG_SIZE(inst), \ + .inst_num = inst, \ + .irq_config = eth_cyclonev_##inst##_irq_config, \ + }; \ + ETH_NET_DEVICE_DT_INST_DEFINE(inst, eth_cyclonev_probe, NULL, \ + ð_cyclonev_##inst##_data, \ + ð_cyclonev_##inst##_cfg, \ + CONFIG_ETH_INIT_PRIORITY, \ + ð_cyclonev_api, \ + NET_ETH_MTU); \ + \ + static void eth_cyclonev_##inst##_irq_config(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(inst), \ + DT_INST_IRQ(inst, priority), eth_cyclonev_isr, \ + DEVICE_DT_INST_GET(inst), \ + 0); \ + irq_enable(DT_INST_IRQN(inst)); \ + } +DT_INST_FOREACH_STATUS_OKAY(CYCLONEV_ETH_INIT) diff --git a/drivers/ethernet/eth_cyclonev_priv.h b/drivers/ethernet/eth_cyclonev_priv.h new file mode 100644 index 00000000000..46b70f5e2c4 --- /dev/null +++ b/drivers/ethernet/eth_cyclonev_priv.h @@ -0,0 +1,504 @@ +#ifndef ETH_CYCLONEV_HEADER +#define ETH_CYCLONEV_HEADER +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2022, Intel Corporation + * Description: + * Driver for the Synopsys DesignWare + * 3504-0 Universal 10/100/1000 Ethernet MAC (DWC_gmac) + * specifically designed for Cyclone V SoC DevKit use only. + */ + +#include +#include + +#define alt_replbits_word(dest, msk, src) \ + (sys_write32((sys_read32(dest) & ~(msk)) | ((src) & (msk)), dest)) + +#define NB_TX_DESCS CONFIG_ETH_CVSX_NB_TX_DESCS +#define NB_RX_DESCS CONFIG_ETH_CVSX_NB_RX_DESCS + +#define ETH_BUFFER_SIZE 1536 + +/* Descriptor Structure */ +struct eth_cyclonev_dma_desc { + uint32_t status; /*!< Status */ + uint32_t control_buffer_size; /*!< Control and Buffer1, Buffer2 sizes */ + uint32_t buffer1_addr; /*!< Buffer1 address pointer */ + uint32_t buffer2_next_desc_addr; /*!< Buffer2 or next desc address pointer */ +}; + +struct eth_cyclonev_priv { + mem_addr_t base_addr; /* Base address */ + uint32_t instance; + uint8_t mac_addr[6]; + uint32_t interrupt_mask; + struct net_if *iface; /* Zephyr net_if Interface Struct (for interface initialisation) */ + uint32_t tx_current_desc_number; + uint32_t rx_current_desc_number; + uint32_t tx_tail; + + uint32_t feature; /* HW feature register */ + /* Tx/Rx Descriptor Rings */ + struct eth_cyclonev_dma_desc tx_desc_ring[NB_TX_DESCS], rx_desc_ring[NB_RX_DESCS]; + uint32_t rxints; /* Tx stats */ + uint32_t txints; /* Rx stats */ + uint8_t rx_buf[ETH_BUFFER_SIZE * NB_RX_DESCS]; /* Receive Buffer */ + uint8_t tx_buf[ETH_BUFFER_SIZE * NB_TX_DESCS]; /* Transmit Buffer */ + + struct k_sem free_tx_descs; + uint8_t running; /* Running state flag */ + uint8_t initialised; /* Initialised state flag */ +}; + +/* + * Reset Manager Regs + */ +/* The base address of the Rstmgr register group. */ +#define RSTMGR_BASE 0xffd05000 + +/* The byte offset of the ALT_RSTMGR_PERMODRST register from the beginning of + * the component. + */ +#define RSTMGR_PERMODRST_OFST 0x14 +/* The address of the ALT_RSTMGR_PERMODRST register. */ +#define RSTMGR_PERMODRST_ADDR 0xFFD05014 +/* The mask used to set the ALT_RSTMGR_PERMODRST_EMAC0 register field value. */ +#define RSTMGR_PERMODRST_EMAC0_SET_MSK 0x00000001 +/* The mask used to set the ALT_RSTMGR_PERMODRST_EMAC1 register field value. */ +#define RSTMGR_PERMODRST_EMAC1_SET_MSK 0x00000002 + +/* + * System Manager Regs + */ +#define SYSMGR_BASE 0xffd08000 +#define SYSMGR_EMAC_ADDR 0xffd08060 +#define SYSMGR_FPGAINTF_INDIV_ADDR 0xffd08004 + +/* The byte offset of the SYSMGR_EMAC register from the beginning of the + * component. + */ +#define SYSMGR_EMAC_OFST 0x60 +/* The byte offset of the SYSMGR_FPGAINTF_INDIV register from the beginning of + * the component. + */ +#define SYSMGR_FPGAINTF_INDIV_OFST 0x4 +/* + * Enumerated value for register field ALT_SYSMGR_EMACn_PHY_INTF_SEL + * + */ +#define SYSMGR_EMAC_PHY_INTF_SEL_E_GMII_MII 0x0 +/* + * Enumerated value for register field ALT_SYSMGR_EMACn_PHY_INTF_SEL + * + */ +#define SYSMGR_EMAC0_PHY_INTF_SEL_E_RGMII 0x1 +#define SYSMGR_EMAC1_PHY_INTF_SEL_E_RGMII 0x4 +/* + * Enumerated value for register field ALT_SYSMGR_EMACn_PHY_INTF_SEL + * + */ +#define SYSMGR_EMAC_PHY_INTF_SEL_E_RMII 0x2 + +/* The mask used to set the ALT_SYSMGR_EMACn_PHY_INTF_SEL register field value. + */ +#define SYSMGR_EMAC0_PHY_INTF_SEL_SET_MSK 0x00000003 +#define SYSMGR_EMAC1_PHY_INTF_SEL_SET_MSK 0x0000000c + +/* The mask used to set the ALT_SYSMGR_FPGAINTF_MODULE_EMAC_0 register field + * value. + */ +#define SYSMGR_FPGAINTF_MODULE_EMAC_0_SET_MSK 0x00000004 + +/* The mask used to set the ALT_SYSMGR_FPGAINTF_MODULE_EMAC_1 register field + * value. + */ +#define SYSMGR_FPGAINTF_MODULE_EMAC_1_SET_MSK 0x00000008 + +/* + * Emac Registers + */ +/* Macros */ +#define EMAC_BASE_ADDRESS DT_INST_REG_ADDR(0) + +#define EMAC_DMAGRP_BUS_MODE_ADDR(base) (uint32_t)((base) + EMAC_DMA_MODE_OFST) /* Bus Mode */ +#define EMAC_DMA_RX_DESC_LIST_ADDR(base) (uint32_t)((base) + EMAC_DMA_RX_DESC_LIST_OFST) +/* Receive Descriptor Address List */ +#define EMAC_DMA_TX_DESC_LIST_ADDR(base) (uint32_t)((base) + EMAC_DMA_TX_DESC_LIST_OFST) +/* Transceive Descriptor Address List */ +#define EMAC_DMAGRP_OPERATION_MODE_ADDR(base) (uint32_t)((base) + EMAC_DMAGRP_OPERATION_MODE_OFST) +/* Operation Mode */ +#define EMAC_DMAGRP_STATUS_ADDR(base) (uint32_t)((base) + EMAC_DMAGRP_STATUS_OFST) /* Status */ +#define EMAC_DMAGRP_DEBUG_ADDR(base) (uint32_t)((base) + EMAC_DMAGRP_DEBUG_OFST) /* Debug */ +#define EMAC_DMA_INT_EN_ADDR(base) (uint32_t)((base) + EMAC_DMA_INT_EN_OFST) +/* Interrupt Enable */ +#define EMAC_DMAGRP_AXI_BUS_MODE_ADDR(base) (uint32_t)((base) + EMAC_DMAGRP_AXI_BUS_MODE_OFST) +/* AXI Bus Mode */ +#define EMAC_DMAGRP_AHB_OR_AXI_STATUS_ADDR(base) \ + (uint32_t)((base) + EMAC_DMAGRP_AHB_OR_AXI_STATUS_OFST) +/* AHB or AXI Status */ +#define GMACGRP_CONTROL_STATUS_ADDR(base) \ + (uint32_t)((base) + \ + EMAC_GMACGRP_SGMII_RGMII_SMII_CONTROL_STATUS_OFST) \ + /* SGMII RGMII SMII Control Status */ +#define EMAC_GMAC_INT_MSK_ADDR(base) (uint32_t)((base) + EMAC_GMAC_INT_MSK_OFST) +/* Interrupt Mask */ +#define EMAC_GMAC_INT_STAT_ADDR(base) (uint32_t)((base) + EMAC_GMAC_INT_STAT_OFST) +/* Interrupt Status */ +#define GMACGRP_MAC_CONFIG_ADDR(base) (uint32_t)((base) + EMAC_GMACGRP_MAC_CONFIGURATION_OFST) +/* MAC Configuration */ +#define EMAC_GMACGRP_MAC_FRAME_FILTER_ADDR(base) \ + (uint32_t)((base) + EMAC_GMACGRP_MAC_FRAME_FILTER_OFST) +/* MAC Frame Filter */ +#define EMAC_GMAC_MAC_ADDR0_HIGH_ADDR(base) (uint32_t)((base) + EMAC_GMAC_MAC_ADDR0_HIGH_OFST) +/* MAC Address 0 High */ +#define EMAC_GMAC_MAC_ADDR0_LOW_ADDR(base) (uint32_t)((base) + EMAC_GMAC_MAC_ADDR0_LOW_OFST) +/* MAC Address 0 Low */ +#define EMAC_GMAC_MAC_ADDR_HIGH_ADDR(base, n) (uint32_t)((base) + EMAC_GMAC_MAC_ADDR_HIGH_OFST(n)) +/* MAC Address 0 High */ +#define EMAC_GMAC_MAC_ADDR_LOW_ADDR(base, n) (uint32_t)((base) + EMAC_GMAC_MAC_ADDR_LOW_OFST(n)) +/* MAC Address 0 High */ +#define EMAC_GMAC_GMII_ADDR_ADDR(base) (uint32_t)((base) + EMAC_GMAC_GMII_ADDR_OFST) +/* GMII Address */ +#define EMAC_GMAC_GMII_DATA_ADDR(base) (uint32_t)((base) + EMAC_GMAC_GMII_DATA_OFST) +/* GMII Data */ +#define EMAC_DMA_TX_POLL_DEMAND_ADDR(base) (uint32_t)((base) + EMAC_DMA_TX_POLL_DEMAND_OFST) +/* Transmit Poll Demand */ +#define EMAC_DMA_RX_POLL_DEMAND_ADDR(base) (uint32_t)((base) + EMAC_DMA_RX_POLL_DEMAND_OFST) +/* Receive Poll Demand */ +#define EMAC_DMA_CURR_HOST_TX_DESC_ADDR(base) (uint32_t)((base) + EMAC_DMA_CURR_HOST_TX_DESC_OFST) +/* Current Host Transmit Descriptor */ +#define EMAC_DMA_CURR_HOST_RX_DESC_ADDR(base) (uint32_t)((base) + EMAC_DMA_CURR_HOST_RX_DESC_OFST) +/* Current Host Receive Descriptor */ +#define EMAC_DMA_CURR_HOST_TX_BUFF_ADDR(base) (uint32_t)((base) + EMAC_DMA_CURR_HOST_TX_BUFF_OFST) +/* Current Host Transmit Buffer Address */ +#define EMAC_DMA_CURR_HOST_RX_BUFF_ADDR(base) (uint32_t)((base) + EMAC_DMA_CURR_HOST_RX_BUFF_OFST) +/* Current Host Receive Buffer Address */ +#define EMAC_DMA_HW_FEATURE_ADDR(base) (uint32_t)((base) + EMAC_DMA_HW_FEATURE_OFST) +/* HW Feature */ + +/* Bus Mode */ +#define EMAC_DMA_MODE_OFST 0x1000 +#define EMAC_DMA_MODE_SWR_SET_MSK 0x00000001 +#define EMAC_DMA_MODE_SWR_GET(value) (((value)&0x00000001) >> 0) +#define EMAC_DMA_MODE_FB_SET_MSK 0x00010000 +#define EMAC_DMA_MODE_RPBL_SET(value) (((value) << 17) & 0x007e0000) +#define EMAC_DMA_MODE_PBL_SET(value) (((value) << 8) & 0x00003f00) +#define EMAC_DMA_MODE_EIGHTXPBL_SET(value) (((value) << 24) & 0x01000000) +#define EMAC_DMA_MODE_AAL_SET_MSK 0x02000000 +#define EMAC_DMA_MODE_USP_SET_MSK 0x00800000 + +/* Receive Descriptor Address List */ +#define EMAC_DMA_RX_DESC_LIST_OFST 0x100c + +/* Transceive Descriptor Address List */ +#define EMAC_DMA_TX_DESC_LIST_OFST 0x1010 + +/* Operation Mode */ +#define EMAC_DMAGRP_OPERATION_MODE_OFST 0x1018 +#define EMAC_DMAGRP_OPERATION_MODE_OSF_SET_MSK 0x00000004 /* Operate on Second Frame */ +#define EMAC_DMAGRP_OPERATION_MODE_TSF_SET_MSK 0x00200000 /* Transmit Store and Forward */ +#define EMAC_DMAGRP_OPERATION_MODE_RSF_SET_MSK 0x02000000 /* Receive Store and Forward */ +#define EMAC_DMAGRP_OPERATION_MODE_FTF_SET_MSK 0x00100000 /* Receive Store and Forward */ +#define EMAC_DMAGRP_OPERATION_MODE_ST_SET_MSK 0x00002000 +#define EMAC_DMAGRP_OPERATION_MODE_SR_SET_MSK 0x00000002 +#define EMAC_DMAGRP_OPERATION_MODE_DT_SET_MSK 0x04000000 /* Ignore frame errors */ + +/* Interrupt Enable */ +#define EMAC_DMA_INT_EN_OFST 0x101C +#define EMAC_DMA_INT_EN_NIE_SET_MSK 0x00010000 +#define EMAC_DMA_INT_EN_AIE_SET_MSK 0x00008000 +#define EMAC_DMA_INT_EN_ERE_SET_MSK 0x00004000 +#define EMAC_DMA_INT_EN_FBE_SET_MSK 0x00002000 +#define EMAC_DMA_INT_EN_ETE_SET_MSK 0x00000400 +#define EMAC_DMA_INT_EN_RWE_SET_MSK 0x00000200 +#define EMAC_DMA_INT_EN_RSE_SET_MSK 0x00000100 +#define EMAC_DMA_INT_EN_RUE_SET_MSK 0x00000080 +#define EMAC_DMA_INT_EN_RIE_SET_MSK 0x00000040 +#define EMAC_DMA_INT_EN_UNE_SET_MSK 0x00000020 +#define EMAC_DMA_INT_EN_OVE_SET_MSK 0x00000010 +#define EMAC_DMA_INT_EN_TJE_SET_MSK 0x00000008 +#define EMAC_DMA_INT_EN_TUE_SET_MSK 0x00000004 +#define EMAC_DMA_INT_EN_TSE_SET_MSK 0x00000002 +#define EMAC_DMA_INT_EN_TIE_SET_MSK 0x00000001 + +/* Status */ +#define EMAC_DMAGRP_STATUS_OFST 0x1014 +#define EMAC_DMAGRP_STATUS_TS_SET_MSK 0x00700000 +#define EMAC_DMAGRP_STATUS_TS_E_SUSPTX 0x00600000 +#define EMAC_DMAGRP_STATUS_RS_SET_MSK 0x000e0000 +#define EMAC_DMAGRP_STATUS_RS_E_SUSPRX 0x00080000 + +#define EMAC_DMAGRP_DEBUG_OFST 0x24 +#define EMAC_DMAGRP_DEBUG_TWCSTS 0x00400000 +#define EMAC_DMAGRP_DEBUG_RWCSTS 0x00000010 +#define EMAC_DMAGRP_DEBUG_RXFSTS_GET(value) (((value)&0x00000300) >> 8) + +/* AXI Bus Mode */ +#define EMAC_DMAGRP_AXI_BUS_MODE_OFST 0x1028 +#define EMAC_DMAGRP_AXI_BUS_MODE_BLEN16_SET_MSK 0x00000008 + +/* AHB or AXI Status */ +#define EMAC_DMAGRP_AHB_OR_AXI_STATUS_OFST 0x102c + +/* MAC Configuration */ + +#define EMAC_GMACGRP_MAC_CONFIGURATION_OFST 0x0000 +#define EMAC_GMACGRP_MAC_CONFIGURATION_IPC_SET_MSK 0x00000400 +#define EMAC_GMACGRP_MAC_CONFIGURATION_JD_SET_MSK 0x00400000 /* Jabber Disable */ +#define EMAC_GMACGRP_MAC_CONFIGURATION_PS_SET_MSK 0x00008000 /* Port Select = MII */ +#define EMAC_GMACGRP_MAC_CONFIGURATION_BE_SET_MSK 0x00200000 /* Frame Burst Enable */ +#define EMAC_GMACGRP_MAC_CONFIGURATION_WD_SET_MSK 0x00800000 /* Watchdog Disable */ +#define EMAC_GMACGRP_MAC_CONFIGURATION_DO_SET_MSK 0x00002000 +#define EMAC_GMACGRP_MAC_CONFIGURATION_TE_SET_MSK 0x00000008 +#define EMAC_GMACGRP_MAC_CONFIGURATION_RE_SET_MSK 0x00000004 +#define EMAC_GMACGRP_MAC_CONFIGURATION_TC_SET_MSK 0x01000000 + +#define EMAC_GMACGRP_MAC_CONFIGURATION_DM_SET_MSK 0x00000800 +#define EMAC_GMACGRP_MAC_CONFIGURATION_FES_SET_MSK 0x00004000 + +/* SGMII RGMII SMII Control Status */ +#define EMAC_GMACGRP_SGMII_RGMII_SMII_CONTROL_STATUS_OFST 0x00d8 +#define EMAC_GMAC_MII_CTL_STAT_LNKSTS_GET(value) (((value)&0x00000008) >> 3) +#define EMAC_GMAC_MII_CTL_STAT_LNKSPEED_GET(value) (((value)&0x00000007) >> 1) +#define EMAC_GMAC_MII_CTL_STAT_LNKMOD_GET(value) ((value)&0x00000001) + +/* Interrupt Mask */ +#define EMAC_GMAC_INT_MSK_OFST 0x003c +#define EMAC_GMAC_INT_STAT_LPIIS_SET_MSK 0x00000400 +#define EMAC_GMAC_INT_STAT_TSIS_SET_MSK 0x00000200 +#define EMAC_GMAC_INT_STAT_RGSMIIIS_SET_MSK 0x00000001 + +/* Interrupt Status (Gmac)*/ +#define EMAC_GMAC_INT_STAT_OFST 0x0038 + +/* MAC Frame Filter */ +#define EMAC_GMACGRP_MAC_FRAME_FILTER_OFST 0x0004 +#define EMAC_GMACGRP_MAC_FRAME_FILTER_PR_SET_MSK 0x00000001 + +/* MAC Address 0 High */ +#define EMAC_GMAC_MAC_ADDR0_HIGH_OFST 0x40 +#define EMAC_GMAC_MAC_ADDR_HIGH_OFST(n) (0x40 + 8 * (n)) +/* MAC Address 0 Low */ +#define EMAC_GMAC_MAC_ADDR0_LOW_OFST 0x44 +#define EMAC_GMAC_MAC_ADDR_LOW_OFST(n) (0x44 + 8 * (n)) + +/* GMII Address */ +#define EMAC_GMAC_GMII_ADDR_OFST 0x10 +#define EMAC_GMAC_GMII_ADDR_PA_SET(value) (((value) << 11) & 0x0000f800) +#define EMAC_GMAC_GMII_ADDR_GR_SET(value) (((value) << 6) & 0x000007c0) +#define EMAC_GMAC_GMII_ADDR_GW_SET_MSK 0x00000002 +#define EMAC_GMAC_GMII_ADDR_GW_CLR_MSK 0xfffffffd +#define EMAC_GMAC_GMII_ADDR_CR_SET(value) (((value) << 2) & 0x0000003c) +#define EMAC_GMAC_GMII_ADDR_GB_SET(value) (((value) << 0) & 0x00000001) +#define EMAC_GMAC_GMII_ADDR_CR_E_DIV102 0x4 +#define EMAC_GMAC_GMII_ADDR_GB_SET_MSK 0x00000001 + +/* GMII Data */ +#define EMAC_GMAC_GMII_DATA_OFST 0x14 + +/* Transmit Poll Demand */ +#define EMAC_DMA_TX_POLL_DEMAND_OFST 0x1004 + +/* Receive Poll Demand */ +#define EMAC_DMA_RX_POLL_DEMAND_OFST 0x1008 + +/* Current Host Transmit Descriptor */ +#define EMAC_DMA_CURR_HOST_TX_DESC_OFST 0x1048 + +/* Current Host Receive Descriptor */ +#define EMAC_DMA_CURR_HOST_RX_DESC_OFST 0x104C + +/* Current Host Transmit Buffer Address */ +#define EMAC_DMA_CURR_HOST_TX_BUFF_OFST 0x1050 + +/* Current Host Receive Buffer Address */ +#define EMAC_DMA_CURR_HOST_RX_BUFF_OFST 0x1054 + +/* HW Feature */ +#define EMAC_DMA_HW_FEATURE_OFST 0x1058 +#define EMAC_DMA_HW_FEATURE_MIISEL 0x00000001 /* 10/100 Mbps support */ +#define EMAC_DMA_HW_FEATURE_GMIISEL 0x00000002 /* 1000 Mbps support */ +#define EMAC_DMA_HW_FEATURE_HDSEL 0x00000004 /* Half-Duplex support */ +#define EMAC_DMA_HW_FEATURE_RXTYP2COE 0x00040000 /* IP Checksum Offload (Type 2) in Rx */ +#define EMAC_DMA_HW_FEATURE_RXTYP1COE 0x00020000 /* IP Checksum Offload (Type 1) in Rx */ +#define EMAC_DMA_HW_FEATURE_TXOESEL 0x00010000 /* Checksum Offload in Tx */ + +/* + * DMA Descriptor Flag Definitions + */ + +/* + * DMA Rx Descriptor + * ------------------------------------------------------------------------------------------- + * RDES0 | OWN(31) | Status [30:0] | + * ------------------------------------------------------------------------------------------- + * RDES1 |CTRL(31)|Reserv[30:29]|Buff2ByteCt[28:16]|CTRL[15:14] + * Reservr(13)|Buff1ByteCt[12:0]| + * ------------------------------------------------------------------------------------------- + * RDES2 | Buffer1 Address [31:0] | + * -------------------------------------------------------------------------------------------- + * RDES3 | Buffer2 Address [31:0] / Next Descriptor Address [31:0] + * | + * -------------------------------------------------------------------------------------------- + */ + +/* Bit definition of RDES0 register: DMA Rx descriptor status register */ +#define ETH_DMARXDESC_OWN ((uint32_t)0x80000000) +/*!< OWN bit: descriptor is owned by DMA engine */ +#define ETH_DMARXDESC_AFM ((uint32_t)0x40000000) +/*!< DA Filter Fail for the rx frame */ +#define ETH_DMARXDESC_FL ((uint32_t)0x3FFF0000) +/*!< Receive descriptor frame length */ +#define ETH_DMARXDESC_ES ((uint32_t)0x00008000) +/*!< Error summary: OR of the following bits: + * DE || OE || IPC || LC || RWT || RE || CE + */ +#define ETH_DMARXDESC_DE ((uint32_t)0x00004000) +/*!< Descriptor error: no more descriptors for receive frame */ +#define ETH_DMARXDESC_SAF ((uint32_t)0x00002000) +/*!< SA Filter Fail for the received frame */ +#define ETH_DMARXDESC_LE ((uint32_t)0x00001000) +/*!< Frame size not matching with length field */ +#define ETH_DMARXDESC_OE ((uint32_t)0x00000800) +/*!< Overflow Error: Frame was damaged due to buffer overflow */ +#define ETH_DMARXDESC_VLAN ((uint32_t)0x00000400) +/*!< VLAN Tag: received frame is a VLAN frame */ +#define ETH_DMARXDESC_FS ((uint32_t)0x00000200) +/*!< First descriptor of the frame */ +#define ETH_DMARXDESC_LS ((uint32_t)0x00000100) +/*!< Last descriptor of the frame */ +#define ETH_DMARXDESC_IPV4HCE ((uint32_t)0x00000080) +/*!< IPC Checksum Error: Rx Ipv4 header checksum error */ +#define ETH_DMARXDESC_LC ((uint32_t)0x00000040) +/*!< Late collision occurred during reception */ +#define ETH_DMARXDESC_FT ((uint32_t)0x00000020) +/*!< Frame type - Ethernet, otherwise 802.3 */ +#define ETH_DMARXDESC_RWT ((uint32_t)0x00000010) +/*!< Receive Watchdog Timeout: watchdog timer expired during reception */ +#define ETH_DMARXDESC_RE ((uint32_t)0x00000008) +/*!< Receive error: error reported by MII interface */ +#define ETH_DMARXDESC_DBE ((uint32_t)0x00000004) +/*!< Dribble bit error: frame contains non int multiple of 8 bits */ +#define ETH_DMARXDESC_CE ((uint32_t)0x00000002) +/*!< CRC error */ +#define ETH_DMARXDESC_MAMPCE ((uint32_t)0x00000001) +/* !< Rx MAC Address/Payload Checksum Error: Rx MAC address matched/ + * Rx Payload Checksum Error + */ + +/* Bit definition of RDES1 register */ +#define ETH_DMARXDESC_DIC ((uint32_t)0x80000000) /*!< Disable Interrupt on Completion */ +#define ETH_DMARXDESC_RBS2 ((uint32_t)0x1FFF0000) /*!< Receive Buffer2 Size */ +#define ETH_DMARXDESC_RER ((uint32_t)0x00008000) /*!< Receive End of Ring */ +#define ETH_DMARXDESC_RCH \ + ((uint32_t)0x00004000) /*!< Second Address Chained \ + */ +#define ETH_DMARXDESC_RBS1 ((uint32_t)0x00001FFF) /*!< Receive Buffer1 Size */ + +/* + *DMA Tx Descriptor + *----------------------------------------------------------------------------------------------- + *TDES0 | OWN(31) | CTRL[30:26] | Reserved[25:24] | CTRL[23:20] | + *Reserved[19:17] | Status[16:0] | + *----------------------------------------------------------------------------------------------- + *TDES1 | Reserved[31:29] | Buffer2 ByteCount[28:16] | Reserved[15:13] | Buffer1 + *ByteCount[12:0] | + *----------------------------------------------------------------------------------------------- + *TDES2 | Buffer1 Address [31:0] + *| + *----------------------------------------------------------------------------------------------- + *TDES3 | Buffer2 Address [31:0] / Next Descriptor Address [31:0] + *| + *----------------------------------------------------------------------------------------------- + */ + +/* Bit definition of TDES0 register: DMA Tx descriptor status register */ +#define ETH_DMATXDESC_OWN ((uint32_t)0x80000000) +/*!< OWN bit: descriptor is owned by DMA engine */ +#define ETH_DMATXDESC_IC ((uint32_t)0x40000000) +/*!< Interrupt on Completion */ +#define ETH_DMATXDESC_LS ((uint32_t)0x20000000) +/*!< Last Segment */ +#define ETH_DMATXDESC_FS ((uint32_t)0x10000000) +/*!< First Segment */ +#define ETH_DMATXDESC_DC ((uint32_t)0x08000000) +/*!< Disable CRC */ +#define ETH_DMATXDESC_DP ((uint32_t)0x04000000) +/*!< Disable Padding */ +#define ETH_DMATXDESC_TTSE ((uint32_t)0x02000000) +/*!< Transmit Time Stamp Enable */ +#define ETH_DMATXDESC_CIC ((uint32_t)0x00C00000) +/*!< Checksum Insertion Control: 4 cases */ +#define ETH_DMATXDESC_CIC_BYPASS ((uint32_t)0x00000000) +/*!< Do Nothing: Checksum Engine is bypassed */ +#define ETH_DMATXDESC_CIC_IPV4HEADER ((uint32_t)0x00400000) +/*!< IPV4 header Checksum Insertion */ +#define ETH_DMATXDESC_CIC_TCPUDPICMP_SEGMENT ((uint32_t)0x00800000) +/*!< TCP/UDP/ICMP Checksum Insertion calculated over segment only */ +#define ETH_DMATXDESC_CIC_TCPUDPICMP_FULL ((uint32_t)0x00C00000) +/*!< TCP/UDP/ICMP Checksum Insertion fully calculated */ +#define ETH_DMATXDESC_TER ((uint32_t)0x00200000) +/*!< Transmit End of Ring */ +#define ETH_DMATXDESC_TCH ((uint32_t)0x00100000) +/*!< Second Address Chained */ +#define ETH_DMATXDESC_TTSS ((uint32_t)0x00020000) +/*!< Tx Time Stamp Status */ +#define ETH_DMATXDESC_IHE ((uint32_t)0x00010000) +/*!< IP Header Error */ +#define ETH_DMATXDESC_ES ((uint32_t)0x00008000) +/*!< Error summary: OR of the following bits: UE||ED||EC||LCO||NC||LCA||FF||JT + */ +#define ETH_DMATXDESC_JT ((uint32_t)0x00004000) +/*!< Jabber Timeout */ +#define ETH_DMATXDESC_FF ((uint32_t)0x00002000) +/*!< Frame Flushed: DMA/MTL flushed the frame due to SW flush */ +#define ETH_DMATXDESC_PCE ((uint32_t)0x00001000) +/*!< Payload Checksum Error */ +#define ETH_DMATXDESC_LCA ((uint32_t)0x00000800) +/*!< Loss of Carrier: carrier lost during transmission */ +#define ETH_DMATXDESC_NC ((uint32_t)0x00000400) +/*!< No Carrier: no carrier signal from the transceiver */ +#define ETH_DMATXDESC_LCO ((uint32_t)0x00000200) +/*!< Late Collision: transmission aborted due to collision */ +#define ETH_DMATXDESC_EC ((uint32_t)0x00000100) +/*!< Excessive Collision: transmission aborted after 16 collisions */ +#define ETH_DMATXDESC_VF ((uint32_t)0x00000080) +/*!< VLAN Frame */ +#define ETH_DMATXDESC_CC ((uint32_t)0x00000078) +/*!< Collision Count */ +#define ETH_DMATXDESC_ED ((uint32_t)0x00000004) +/*!< Excessive Deferral */ +#define ETH_DMATXDESC_UF ((uint32_t)0x00000002) +/*!< Underflow Error: late data arrival from the memory */ +#define ETH_DMATXDESC_DB ((uint32_t)0x00000001) +/*!< Deferred Bit */ + +/* Bit definition of TDES1 register */ +#define ETH_DMATXDESC_TBS2 \ + ((uint32_t)0x1FFF0000) /*!< Transmit Buffer2 Size \ + */ +#define ETH_DMATXDESC_TBS1 \ + ((uint32_t)0x00001FFF) /*!< Transmit Buffer1 Size \ + */ + +/* Bit definition of TDES2 register */ +#define ETH_DMATXDESC_B1AP ((uint32_t)0xFFFFFFFF) /*!< Buffer1 Address Pointer */ + +/* Bit definition of TDES3 register */ +#define ETH_DMATXDESC_B2AP ((uint32_t)0xFFFFFFFF) /*!< Buffer2 Address Pointer */ + + +static const uint32_t Rstmgr_Permodrst_Emac_Set_Msk[] = {RSTMGR_PERMODRST_EMAC0_SET_MSK, + RSTMGR_PERMODRST_EMAC1_SET_MSK}; + +static const uint32_t Sysmgr_Core_Emac_Phy_Intf_Sel_Set_Msk[] = {SYSMGR_EMAC0_PHY_INTF_SEL_SET_MSK, + SYSMGR_EMAC1_PHY_INTF_SEL_SET_MSK}; + +static const uint32_t Sysmgr_Fpgaintf_En_3_Emac_Set_Msk[] = {SYSMGR_FPGAINTF_MODULE_EMAC_0_SET_MSK, + SYSMGR_FPGAINTF_MODULE_EMAC_1_SET_MSK}; + +static const uint32_t Sysmgr_Emac_Phy_Intf_Sel_E_Rgmii[] = {SYSMGR_EMAC0_PHY_INTF_SEL_E_RGMII, + SYSMGR_EMAC1_PHY_INTF_SEL_E_RGMII}; + +#endif diff --git a/drivers/ethernet/phy_cyclonev.c b/drivers/ethernet/phy_cyclonev.c new file mode 100644 index 00000000000..a5c1541b99d --- /dev/null +++ b/drivers/ethernet/phy_cyclonev.c @@ -0,0 +1,371 @@ +#ifndef PHY_CYCLONEV_SRC +#define PHY_CYCLONEV_SRC +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2022, Intel Corporation + * Description: + * Driver for the PHY KSZ9021RL/RN Datasheet:(https://ww1.microchip.com/ + * downloads/en/DeviceDoc/KSZ9021RL-RN-Data-Sheet-DS00003050A.pdf) + * specifically designed for Cyclone V SoC DevKit use only. + */ + +/* PHY */ +/* According to default Cyclone V DevKit Bootstrap Encoding Scheme */ +#include "eth_cyclonev_priv.h" + +#include + +#include + +#include + +#define PHY_ADDR (4) + +/* PHY_Read_write_Timeouts */ +#define PHY_READ_TO ((uint32_t)0x0004FFFF) +#define PHY_WRITE_TO ((uint32_t)0x0004FFFF) + +/* Speed and Duplex mask values */ +#define PHY_SPEED_100 (0x0020) +#define PHY_SPEED_1000 (0x0040) + +#define PHY_CLK_AND_CONTROL_PAD_SKEW_VALUE 0xa0d0 +#define PHY_RX_DATA_PAD_SKEW_VALUE 0x0000 + +/* Write/read to/from extended registers */ +#define MII_KSZPHY_EXTREG 0x0b +#define KSZPHY_EXTREG_WRITE 0x8000 +#define MII_KSZPHY_EXTREG_WRITE 0x0c +#define MII_KSZPHY_EXTREG_READ 0x0d + +/* PHY Regs */ + +/* Basic Control Register */ +#define PHY_BCR (0) +#define PHY_RESET BIT(15) /* Do a PHY reset */ +#define PHY_AUTONEGOTIATION BIT(12) +#define PHY_RESTART_AUTONEGOTIATION BIT(9) + +/* Basic Status Register */ +#define PHY_BSR BIT(0) +#define PHY_AUTOCAP BIT(30) /* Auto-negotiation capability */ +#define PHY_LINKED_STATUS BIT(2) +#define PHY_AUTONEGO_COMPLETE BIT(5) + +/* Auto-Negotiation Advertisement */ +#define PHY_AUTON (4) +#define PHYANA_10BASET BIT(5) +#define PHYANA_10BASETFD BIT(6) +#define PHYANA_100BASETX BIT(7) +#define PHYANA_100BASETXFD BIT(8) +#define PHYASYMETRIC_PAUSE BIT(11) + +/* 1000Base-T Control */ +#define PHY_1GCTL (9) + +/* PHY Control Register */ +#define PHY_CR (31) +#define PHY_DUPLEX_STATUS (0x0008) +#define PHYADVERTISE_1000FULL BIT(9) +#define PHYADVERTISE_1000HALF BIT(8) + +/* Extended registers */ +#define MII_KSZPHY_CLK_CONTROL_PAD_SKEW 0x104 +#define MII_KSZPHY_RX_DATA_PAD_SKEW 0x105 +#define MII_KSZPHY_TX_DATA_PAD_SKEW 0x106 + + +int alt_eth_phy_write_register(uint16_t emac_instance, uint16_t phy_reg, + uint16_t phy_value, struct eth_cyclonev_priv *p); +int alt_eth_phy_read_register(uint16_t emac_instance, uint16_t phy_reg, + uint16_t *rdval, struct eth_cyclonev_priv *p); +int alt_eth_phy_write_register_extended(uint16_t emac_instance, uint16_t phy_reg, + uint16_t phy_value, struct eth_cyclonev_priv *p); +int alt_eth_phy_read_register_extended(uint16_t emac_instance, uint16_t phy_reg, + uint16_t *rdval, struct eth_cyclonev_priv *p); +int alt_eth_phy_config(uint16_t instance, struct eth_cyclonev_priv *p); +int alt_eth_phy_reset(uint16_t instance, struct eth_cyclonev_priv *p); +int alt_eth_phy_get_duplex_and_speed(uint16_t *phy_duplex_status, uint16_t *phy_speed, + uint16_t instance, struct eth_cyclonev_priv *p); + +int alt_eth_phy_write_register(uint16_t emac_instance, uint16_t phy_reg, + uint16_t phy_value, struct eth_cyclonev_priv *p) +{ + uint16_t tmpreg = 0; + volatile uint16_t timeout = 0; + uint16_t phy_addr; + + if (emac_instance > 1) { + return -1; + } + + phy_addr = PHY_ADDR; + + /* Prepare the MII address register value */ + tmpreg = 0; + /* Set the PHY device address */ + tmpreg |= EMAC_GMAC_GMII_ADDR_PA_SET(phy_addr); + /* Set the PHY register address */ + tmpreg |= EMAC_GMAC_GMII_ADDR_GR_SET(phy_reg); + /* Set the write mode */ + tmpreg |= EMAC_GMAC_GMII_ADDR_GW_SET_MSK; + /* Set the clock divider */ + tmpreg |= EMAC_GMAC_GMII_ADDR_CR_SET(EMAC_GMAC_GMII_ADDR_CR_E_DIV102); + /* Set the MII Busy bit */ + tmpreg |= EMAC_GMAC_GMII_ADDR_GB_SET(EMAC_GMAC_GMII_ADDR_GB_SET_MSK); + + /* Give the value to the MII data register */ + sys_write32(phy_value & 0xffff, EMAC_GMAC_GMII_DATA_ADDR(p->base_addr)); + /* Write the result value into the MII Address register */ + sys_write32(tmpreg & 0xffff, EMAC_GMAC_GMII_ADDR_ADDR(p->base_addr)); + + + /* Check the Busy flag */ + do { + timeout++; + tmpreg = sys_read32(EMAC_GMAC_GMII_ADDR_ADDR(p->base_addr)); + } while ((tmpreg & EMAC_GMAC_GMII_ADDR_GB_SET_MSK) && (timeout < PHY_WRITE_TO)); + + /* Return ERROR in case of timeout */ + if (timeout == PHY_WRITE_TO) { + return -1; + } + + /* Return SUCCESS */ + return 0; +} + +int alt_eth_phy_read_register(uint16_t emac_instance, uint16_t phy_reg, uint16_t *rdval, + struct eth_cyclonev_priv *p) +{ + uint16_t tmpreg = 0; + volatile uint16_t timeout = 0; + uint16_t phy_addr; + + if (emac_instance > 1) { + return -1; + } + + phy_addr = PHY_ADDR; + + /* Prepare the MII address register value */ + tmpreg = 0; + /* Set the PHY device address */ + tmpreg |= EMAC_GMAC_GMII_ADDR_PA_SET(phy_addr); + /* Set the PHY register address */ + tmpreg |= EMAC_GMAC_GMII_ADDR_GR_SET(phy_reg); + /* Set the read mode */ + tmpreg &= EMAC_GMAC_GMII_ADDR_GW_CLR_MSK; + /* Set the clock divider */ + tmpreg |= EMAC_GMAC_GMII_ADDR_CR_SET(EMAC_GMAC_GMII_ADDR_CR_E_DIV102); + /* Set the MII Busy bit */ + tmpreg |= EMAC_GMAC_GMII_ADDR_GB_SET(EMAC_GMAC_GMII_ADDR_GB_SET_MSK); + + /* Write the result value into the MII Address register */ + sys_write32(tmpreg & 0xffff, EMAC_GMAC_GMII_ADDR_ADDR(p->base_addr)); + + /* Check the Busy flag */ + do { + timeout++; + tmpreg = sys_read32(EMAC_GMAC_GMII_ADDR_ADDR(p->base_addr)); + } while ((tmpreg & EMAC_GMAC_GMII_ADDR_GB_SET_MSK) && (timeout < PHY_READ_TO)); + + /* Return ERROR in case of timeout */ + if (timeout == PHY_READ_TO) { + return -1; + } + + /* Return data register value */ + *rdval = sys_read32(EMAC_GMAC_GMII_DATA_ADDR(p->base_addr)); + + return 0; +} + +int alt_eth_phy_write_register_extended(uint16_t emac_instance, uint16_t phy_reg, + uint16_t phy_value, struct eth_cyclonev_priv *p) +{ + int rc; + + rc = alt_eth_phy_write_register(emac_instance, MII_KSZPHY_EXTREG, + KSZPHY_EXTREG_WRITE | phy_reg, p); + + if (rc == -1) { + return rc; + } + + rc = alt_eth_phy_write_register(emac_instance, MII_KSZPHY_EXTREG_WRITE, phy_value, p); + return rc; +} + +int alt_eth_phy_read_register_extended(uint16_t emac_instance, uint16_t phy_reg, uint16_t *rdval, + struct eth_cyclonev_priv *p) +{ + int rc; + + rc = alt_eth_phy_write_register(emac_instance, MII_KSZPHY_EXTREG, phy_reg, p); + + if (rc == -1) { + return rc; + } + k_sleep(K_MSEC(1)); + + rc = alt_eth_phy_read_register(emac_instance, MII_KSZPHY_EXTREG_READ, rdval, p); + + return rc; +} + +int alt_eth_phy_config(uint16_t instance, struct eth_cyclonev_priv *p) +{ + + int rc; + uint16_t rdval, timeout; + /*-------------------- Configure the PHY skew values ----------------*/ + + rc = alt_eth_phy_write_register_extended(instance, MII_KSZPHY_CLK_CONTROL_PAD_SKEW, + PHY_CLK_AND_CONTROL_PAD_SKEW_VALUE, p); + if (rc == -1) { + return rc; + } + + rc = alt_eth_phy_write_register_extended(instance, MII_KSZPHY_RX_DATA_PAD_SKEW, + PHY_RX_DATA_PAD_SKEW_VALUE, p); + if (rc == -1) { + return rc; + } + + /* Implement Auto-negotiation Process */ + + /* Check PHY Status if auto-negotiation is supported */ + rc = alt_eth_phy_read_register(instance, PHY_BSR, &rdval, p); + if (((rdval & PHY_AUTOCAP) == 0) || (rc == -1)) { + return -1; + } + + /* Set Advertise capabilities for 10Base-T/ + *10Base-T full-duplex/100Base-T/100Base-T full-duplex + */ + rc = alt_eth_phy_read_register(instance, PHY_AUTON, &rdval, p); + if (rc == -1) { + return rc; + } + + rdval |= (PHYANA_10BASET | PHYANA_10BASETFD | PHYANA_100BASETX | PHYANA_100BASETXFD | + PHYASYMETRIC_PAUSE); + rc = alt_eth_phy_write_register(instance, PHY_AUTON, rdval, p); + if (rc == -1) { + return rc; + } + + /* Set Advertise capabilities for 1000 Base-T/1000 Base-T full-duplex */ + + rc = alt_eth_phy_write_register(instance, PHY_1GCTL, + PHYADVERTISE_1000FULL | PHYADVERTISE_1000HALF, p); + if (rc == -1) { + return rc; + } + + /* Wait for linked status... */ + timeout = 0; + do { + timeout++; + rc = alt_eth_phy_read_register(instance, PHY_BSR, &rdval, p); + } while (!(rdval & PHY_LINKED_STATUS) && (timeout < PHY_READ_TO) && (rc == 0)); + + /* Return ERROR in case of timeout */ + if ((timeout == PHY_READ_TO) || (rc == -1)) { + LOG_ERR("Error Link Down\n"); + return -1; + } + LOG_INF("Link is up!"); + + /* Configure the PHY for AutoNegotiate */ + rc = alt_eth_phy_read_register(instance, PHY_BCR, &rdval, p); + if (rc == -1) { + return rc; + } + + rdval |= PHY_AUTONEGOTIATION; + rdval |= PHY_RESTART_AUTONEGOTIATION; + rc = alt_eth_phy_write_register(instance, PHY_BCR, rdval, p); + if (rc == -1) { + return rc; + } + + /* Wait until the auto-negotiation is completed */ + timeout = 0; + do { + timeout++; + rc = alt_eth_phy_read_register(instance, PHY_BSR, &rdval, p); + } while (!(rdval & PHY_AUTONEGO_COMPLETE) && (timeout < PHY_READ_TO) && (rc == 0)); + + /* Return ERROR in case of timeout */ + if ((timeout == PHY_READ_TO) || (rc == -1)) { + alt_eth_phy_read_register(instance, PHY_BSR, &rdval, p); + LOG_ERR("Auto Negotiation: Status reg = 0x%x\n", rdval); + return -1; + } + LOG_INF("Auto Negotiation Complete!"); + + return rc; +}; + +int alt_eth_phy_reset(uint16_t instance, struct eth_cyclonev_priv *p) +{ + int i; + int rc; + uint16_t rdval; + + /* Put the PHY in reset mode */ + if ((alt_eth_phy_write_register(instance, PHY_BCR, PHY_RESET, p)) != 0) { + /* Return ERROR in case of write timeout */ + return -1; + } + + /* Wait for the reset to clear */ + for (i = 0; i < 10; i++) { + k_sleep(K_MSEC(10)); + rc = alt_eth_phy_read_register(instance, PHY_BCR, &rdval, p); + if (((rdval & PHY_RESET) == 0) || (rc == -1)) { + break; + } + } + + if (i == 10) { + return -1; + } + /* Delay to assure PHY reset */ + k_sleep(K_MSEC(10)); + + return rc; +}; + +int alt_eth_phy_get_duplex_and_speed(uint16_t *phy_duplex_status, uint16_t *phy_speed, + uint16_t instance, struct eth_cyclonev_priv *p) +{ + + LOG_DBG("PHY: func_alt_eth_phy_get_duplex_and_speed\n"); + uint16_t regval = 0; + int rc; + + rc = alt_eth_phy_read_register(instance, PHY_CR, ®val, p); + + if (regval & PHY_DUPLEX_STATUS) { + *phy_duplex_status = 1; + } else { + *phy_duplex_status = 0; + } + + if (regval & PHY_SPEED_100) { + *phy_speed = 100; + } else { + if (regval & PHY_SPEED_1000) { + *phy_speed = 1000; + } else { + *phy_speed = 10; + } + } + + return rc; +} + +#endif