From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 73709A0C4B; Sat, 13 Nov 2021 05:32:09 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 3EF3A41141; Sat, 13 Nov 2021 05:32:04 +0100 (CET) Received: from inva020.nxp.com (inva020.nxp.com [92.121.34.13]) by mails.dpdk.org (Postfix) with ESMTP id 30FF74113E for ; Sat, 13 Nov 2021 05:32:01 +0100 (CET) Received: from inva020.nxp.com (localhost [127.0.0.1]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id BFCC31A04A5; Sat, 13 Nov 2021 05:32:00 +0100 (CET) Received: from aprdc01srsp001v.ap-rdc01.nxp.com (aprdc01srsp001v.ap-rdc01.nxp.com [165.114.16.16]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 5981E1A048F; Sat, 13 Nov 2021 05:32:00 +0100 (CET) Received: from lsv03186.swis.in-blr01.nxp.com (lsv03186.swis.in-blr01.nxp.com [92.120.146.182]) by aprdc01srsp001v.ap-rdc01.nxp.com (Postfix) with ESMTP id 45D2F183F0C0; Sat, 13 Nov 2021 12:31:59 +0800 (+08) From: Apeksha Gupta To: ferruh.yigit@intel.com, david.marchand@redhat.com, andrew.rybchenko@oktetlabs.ru Cc: dev@dpdk.org, sachin.saxena@nxp.com, hemant.agrawal@nxp.com, Apeksha Gupta Subject: [PATCH v10 2/5] net/enetfec: add UIO support Date: Sat, 13 Nov 2021 10:01:38 +0530 Message-Id: <20211113043141.18888-3-apeksha.gupta@nxp.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20211113043141.18888-1-apeksha.gupta@nxp.com> References: <20211110074829.16654-2-apeksha.gupta@nxp.com> <20211113043141.18888-1-apeksha.gupta@nxp.com> X-Virus-Scanned: ClamAV using ClamSMTP X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Implemented the fec-uio driver in kernel. enetfec PMD uses UIO interface to interact with "fec-uio" driver implemented in kernel for PHY initialisation and for mapping the allocated memory of register & BD from kernel to DPDK which gives access to non-cacheable memory for BD. Signed-off-by: Sachin Saxena Signed-off-by: Apeksha Gupta --- drivers/net/enetfec/enet_ethdev.c | 209 ++++++++++++++++++++++ drivers/net/enetfec/enet_ethdev.h | 31 ++++ drivers/net/enetfec/enet_regs.h | 106 +++++++++++ drivers/net/enetfec/enet_uio.c | 284 ++++++++++++++++++++++++++++++ drivers/net/enetfec/enet_uio.h | 64 +++++++ drivers/net/enetfec/meson.build | 3 +- 6 files changed, 696 insertions(+), 1 deletion(-) create mode 100644 drivers/net/enetfec/enet_regs.h create mode 100644 drivers/net/enetfec/enet_uio.c create mode 100644 drivers/net/enetfec/enet_uio.h diff --git a/drivers/net/enetfec/enet_ethdev.c b/drivers/net/enetfec/enet_ethdev.c index 56bd199191..8bed091efe 100644 --- a/drivers/net/enetfec/enet_ethdev.c +++ b/drivers/net/enetfec/enet_ethdev.c @@ -13,14 +13,192 @@ #include #include #include +#include #include "enet_pmd_logs.h" #include "enet_ethdev.h" +#include "enet_regs.h" +#include "enet_uio.h" #define ENETFEC_NAME_PMD net_enetfec +/* FEC receive acceleration */ +#define ENETFEC_RACC_IPDIS RTE_BIT32(1) +#define ENETFEC_RACC_PRODIS RTE_BIT32(2) +#define ENETFEC_RACC_SHIFT16 RTE_BIT32(7) +#define ENETFEC_RACC_OPTIONS (ENETFEC_RACC_IPDIS | \ + ENETFEC_RACC_PRODIS) + +#define ENETFEC_PAUSE_FLAG_AUTONEG 0x1 +#define ENETFEC_PAUSE_FLAG_ENABLE 0x2 + +/* Pause frame field and FIFO threshold */ +#define ENETFEC_FCE RTE_BIT32(5) +#define ENETFEC_RSEM_V 0x84 +#define ENETFEC_RSFL_V 16 +#define ENETFEC_RAEM_V 0x8 +#define ENETFEC_RAFL_V 0x8 +#define ENETFEC_OPD_V 0xFFF0 + +#define NUM_OF_BD_QUEUES 6 + +/* + * This function is called to start or restart the ENETFEC during a link + * change, transmit timeout, or to reconfigure the ENETFEC. The network + * packet processing for this device must be stopped before this call. + */ +static void +enetfec_restart(struct rte_eth_dev *dev) +{ + struct enetfec_private *fep = dev->data->dev_private; + uint32_t rcntl = OPT_FRAME_SIZE | 0x04; + uint32_t ecntl = ENETFEC_ETHEREN; + uint32_t val; + + /* Clear any outstanding interrupt. */ + writel(0xffffffff, (uint8_t *)fep->hw_baseaddr_v + ENETFEC_EIR); + + /* Enable MII mode */ + if (fep->full_duplex == FULL_DUPLEX) { + /* FD enable */ + rte_write32(rte_cpu_to_le_32(0x04), + (uint8_t *)fep->hw_baseaddr_v + ENETFEC_TCR); + } else { + /* No Rcv on Xmit */ + rcntl |= 0x02; + rte_write32(0, (uint8_t *)fep->hw_baseaddr_v + ENETFEC_TCR); + } + + if (fep->quirks & QUIRK_RACC) { + val = rte_read32((uint8_t *)fep->hw_baseaddr_v + ENETFEC_RACC); + /* align IP header */ + val |= ENETFEC_RACC_SHIFT16; + val &= ~ENETFEC_RACC_OPTIONS; + rte_write32(rte_cpu_to_le_32(val), + (uint8_t *)fep->hw_baseaddr_v + ENETFEC_RACC); + rte_write32(rte_cpu_to_le_32(PKT_MAX_BUF_SIZE), + (uint8_t *)fep->hw_baseaddr_v + ENETFEC_FRAME_TRL); + } + + /* + * The phy interface and speed need to get configured + * differently on enet-mac. + */ + if (fep->quirks & QUIRK_HAS_ENETFEC_MAC) { + /* Enable flow control and length check */ + rcntl |= 0x40000000 | 0x00000020; + + /* RGMII, RMII or MII */ + rcntl |= RTE_BIT32(6); + ecntl |= RTE_BIT32(5); + } + + /* enable pause frame*/ + if ((fep->flag_pause & ENETFEC_PAUSE_FLAG_ENABLE) || + ((fep->flag_pause & ENETFEC_PAUSE_FLAG_AUTONEG) + /*&& ndev->phydev && ndev->phydev->pause*/)) { + rcntl |= ENETFEC_FCE; + + /* set FIFO threshold parameter to reduce overrun */ + rte_write32(rte_cpu_to_le_32(ENETFEC_RSEM_V), + (uint8_t *)fep->hw_baseaddr_v + ENETFEC_R_FIFO_SEM); + rte_write32(rte_cpu_to_le_32(ENETFEC_RSFL_V), + (uint8_t *)fep->hw_baseaddr_v + ENETFEC_R_FIFO_SFL); + rte_write32(rte_cpu_to_le_32(ENETFEC_RAEM_V), + (uint8_t *)fep->hw_baseaddr_v + ENETFEC_R_FIFO_AEM); + rte_write32(rte_cpu_to_le_32(ENETFEC_RAFL_V), + (uint8_t *)fep->hw_baseaddr_v + ENETFEC_R_FIFO_AFL); + + /* OPD */ + rte_write32(rte_cpu_to_le_32(ENETFEC_OPD_V), + (uint8_t *)fep->hw_baseaddr_v + ENETFEC_OPD); + } else { + rcntl &= ~ENETFEC_FCE; + } + + rte_write32(rte_cpu_to_le_32(rcntl), + (uint8_t *)fep->hw_baseaddr_v + ENETFEC_RCR); + + rte_write32(0, (uint8_t *)fep->hw_baseaddr_v + ENETFEC_IAUR); + rte_write32(0, (uint8_t *)fep->hw_baseaddr_v + ENETFEC_IALR); + + if (fep->quirks & QUIRK_HAS_ENETFEC_MAC) { + /* enable ENETFEC endian swap */ + ecntl |= (1 << 8); + /* enable ENETFEC store and forward mode */ + rte_write32(rte_cpu_to_le_32(1 << 8), + (uint8_t *)fep->hw_baseaddr_v + ENETFEC_TFWR); + } + if (fep->bufdesc_ex) + ecntl |= (1 << 4); + if (fep->quirks & QUIRK_SUPPORT_DELAYED_CLKS && + fep->rgmii_txc_delay) + ecntl |= ENETFEC_TXC_DLY; + if (fep->quirks & QUIRK_SUPPORT_DELAYED_CLKS && + fep->rgmii_rxc_delay) + ecntl |= ENETFEC_RXC_DLY; + /* Enable the MIB statistic event counters */ + rte_write32(0, (uint8_t *)fep->hw_baseaddr_v + ENETFEC_MIBC); + + ecntl |= 0x70000000; + fep->enetfec_e_cntl = ecntl; + /* And last, enable the transmit and receive processing */ + rte_write32(rte_cpu_to_le_32(ecntl), + (uint8_t *)fep->hw_baseaddr_v + ENETFEC_ECR); + rte_delay_us(10); +} + +static int +enetfec_eth_configure(struct rte_eth_dev *dev) +{ + if (dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_KEEP_CRC) + ENETFEC_PMD_ERR("PMD does not support KEEP_CRC offload"); + + return 0; +} + +static int +enetfec_eth_start(struct rte_eth_dev *dev) +{ + enetfec_restart(dev); + + return 0; +} + +/* ENETFEC disable function. + * @param[in] base ENETFEC base address + */ +static void +enetfec_disable(struct enetfec_private *fep) +{ + rte_write32(rte_read32((uint8_t *)fep->hw_baseaddr_v + ENETFEC_ECR) + & ~(fep->enetfec_e_cntl), + (uint8_t *)fep->hw_baseaddr_v + ENETFEC_ECR); +} + +static int +enetfec_eth_stop(struct rte_eth_dev *dev) +{ + struct enetfec_private *fep = dev->data->dev_private; + + dev->data->dev_started = 0; + enetfec_disable(fep); + + return 0; +} + +static const struct eth_dev_ops enetfec_ops = { + .dev_configure = enetfec_eth_configure, + .dev_start = enetfec_eth_start, + .dev_stop = enetfec_eth_stop +}; + static int enetfec_eth_init(struct rte_eth_dev *dev) { + struct enetfec_private *fep = dev->data->dev_private; + + fep->full_duplex = FULL_DUPLEX; + dev->dev_ops = &enetfec_ops; rte_eth_dev_probing_finish(dev); return 0; } @@ -32,6 +210,8 @@ pmd_enetfec_probe(struct rte_vdev_device *vdev) struct enetfec_private *fep; const char *name; int rc; + int i; + unsigned int bdsize; name = rte_vdev_device_name(vdev); ENETFEC_PMD_LOG(INFO, "Initializing pmd_fec for %s", name); @@ -43,6 +223,35 @@ pmd_enetfec_probe(struct rte_vdev_device *vdev) /* setup board info structure */ fep = dev->data->dev_private; fep->dev = dev; + + fep->max_rx_queues = ENETFEC_MAX_Q; + fep->max_tx_queues = ENETFEC_MAX_Q; + fep->quirks = QUIRK_HAS_ENETFEC_MAC | QUIRK_GBIT + | QUIRK_RACC; + + rc = enetfec_configure(); + if (rc != 0) + return -ENOMEM; + rc = config_enetfec_uio(fep); + if (rc != 0) + return -ENOMEM; + + /* Get the BD size for distributing among six queues */ + bdsize = (fep->bd_size) / NUM_OF_BD_QUEUES; + + for (i = 0; i < fep->max_tx_queues; i++) { + fep->dma_baseaddr_t[i] = fep->bd_addr_v; + fep->bd_addr_p_t[i] = fep->bd_addr_p; + fep->bd_addr_v = (uint8_t *)fep->bd_addr_v + bdsize; + fep->bd_addr_p = fep->bd_addr_p + bdsize; + } + for (i = 0; i < fep->max_rx_queues; i++) { + fep->dma_baseaddr_r[i] = fep->bd_addr_v; + fep->bd_addr_p_r[i] = fep->bd_addr_p; + fep->bd_addr_v = (uint8_t *)fep->bd_addr_v + bdsize; + fep->bd_addr_p = fep->bd_addr_p + bdsize; + } + rc = enetfec_eth_init(dev); if (rc) goto failed_init; diff --git a/drivers/net/enetfec/enet_ethdev.h b/drivers/net/enetfec/enet_ethdev.h index 7e941da972..4d671e6d45 100644 --- a/drivers/net/enetfec/enet_ethdev.h +++ b/drivers/net/enetfec/enet_ethdev.h @@ -5,14 +5,45 @@ #ifndef __ENETFEC_ETHDEV_H__ #define __ENETFEC_ETHDEV_H__ +#include + +/* full duplex */ +#define FULL_DUPLEX 0x00 + +#define PKT_MAX_BUF_SIZE 1984 +#define OPT_FRAME_SIZE (PKT_MAX_BUF_SIZE << 16) + /* * ENETFEC can support 1 rx and tx queue.. */ #define ENETFEC_MAX_Q 1 +#define writel(v, p) ({*(volatile unsigned int *)(p) = (v); }) +#define readl(p) rte_read32(p) + struct enetfec_private { struct rte_eth_dev *dev; + int full_duplex; + int flag_pause; + uint32_t quirks; + uint32_t cbus_size; + uint32_t enetfec_e_cntl; + uint16_t max_rx_queues; + uint16_t max_tx_queues; + unsigned int reg_size; + unsigned int bd_size; + bool bufdesc_ex; + bool rgmii_txc_delay; + bool rgmii_rxc_delay; + void *hw_baseaddr_v; + void *bd_addr_v; + uint32_t hw_baseaddr_p; + uint32_t bd_addr_p; + uint32_t bd_addr_p_r[ENETFEC_MAX_Q]; + uint32_t bd_addr_p_t[ENETFEC_MAX_Q]; + void *dma_baseaddr_r[ENETFEC_MAX_Q]; + void *dma_baseaddr_t[ENETFEC_MAX_Q]; }; #endif /*__ENETFEC_ETHDEV_H__*/ diff --git a/drivers/net/enetfec/enet_regs.h b/drivers/net/enetfec/enet_regs.h new file mode 100644 index 0000000000..5415ed77ea --- /dev/null +++ b/drivers/net/enetfec/enet_regs.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2020 NXP + */ + +#ifndef __ENETFEC_REGS_H +#define __ENETFEC_REGS_H + +/* Ethernet receive use control and status of buffer descriptor + */ +#define RX_BD_TR ((ushort)0x0001) /* Truncated */ +#define RX_BD_OV ((ushort)0x0002) /* Over-run */ +#define RX_BD_CR ((ushort)0x0004) /* CRC or Frame error */ +#define RX_BD_SH ((ushort)0x0008) /* Reserved */ +#define RX_BD_NO ((ushort)0x0010) /* Rcvd non-octet aligned frame */ +#define RX_BD_LG ((ushort)0x0020) /* Rcvd frame length voilation */ +#define RX_BD_FIRST ((ushort)0x0400) /* Reserved */ +#define RX_BD_LAST ((ushort)0x0800) /* last buffer in the frame */ +#define RX_BD_INT 0x00800000 +#define RX_BD_ICE 0x00000020 +#define RX_BD_PCR 0x00000010 + +/* + * 0 The next BD in consecutive location + * 1 The next BD in ENETFECn_RDSR. + */ +#define RX_BD_WRAP ((ushort)0x2000) +#define RX_BD_EMPTY ((ushort)0x8000) /* BD is empty */ +#define RX_BD_STATS ((ushort)0x013f) /* All buffer descriptor status bits */ + +/* Ethernet transmit use control and status of buffer descriptor */ +#define TX_BD_TC ((ushort)0x0400) /* Transmit CRC */ +#define TX_BD_LAST ((ushort)0x0800) /* Last in frame */ +#define TX_BD_READY ((ushort)0x8000) /* Data is ready */ +#define TX_BD_STATS ((ushort)0x0fff) /* All buffer descriptor status bits */ +#define TX_BD_WRAP ((ushort)0x2000) + +/* Ethernet transmit use control and status of enhanced buffer descriptor */ +#define TX_BD_IINS 0x08000000 +#define TX_BD_PINS 0x10000000 + +#define ENETFEC_RD_START(X) (((X) == 1) ? ENETFEC_RD_START_1 : \ + (((X) == 2) ? \ + ENETFEC_RD_START_2 : ENETFEC_RD_START_0)) +#define ENETFEC_TD_START(X) (((X) == 1) ? ENETFEC_TD_START_1 : \ + (((X) == 2) ? \ + ENETFEC_TD_START_2 : ENETFEC_TD_START_0)) +#define ENETFEC_MRB_SIZE(X) (((X) == 1) ? ENETFEC_MRB_SIZE_1 : \ + (((X) == 2) ? \ + ENETFEC_MRB_SIZE_2 : ENETFEC_MRB_SIZE_0)) + +#define ENETFEC_ETHEREN ((uint)0x00000002) +#define ENETFEC_TXC_DLY ((uint)0x00010000) +#define ENETFEC_RXC_DLY ((uint)0x00020000) + +/* ENETFEC MAC is in controller */ +#define QUIRK_HAS_ENETFEC_MAC (1 << 0) +/* GBIT supported in controller */ +#define QUIRK_GBIT (1 << 3) +/* RACC register supported by controller */ +#define QUIRK_RACC (1 << 12) +/* i.MX8 ENETFEC IP version added the feature to generate the delayed TXC or + * RXC. For its implementation, ENETFEC uses synchronized clocks (250MHz) for + * generating delay of 2ns. + */ +#define QUIRK_SUPPORT_DELAYED_CLKS (1 << 18) + +#define ENETFEC_EIR 0x004 /* Interrupt event register */ +#define ENETFEC_EIMR 0x008 /* Interrupt mask register */ +#define ENETFEC_RDAR_0 0x010 /* Receive descriptor active register ring0 */ +#define ENETFEC_TDAR_0 0x014 /* Transmit descriptor active register ring0 */ +#define ENETFEC_ECR 0x024 /* Ethernet control register */ +#define ENETFEC_MSCR 0x044 /* MII speed control register */ +#define ENETFEC_MIBC 0x064 /* MIB control and status register */ +#define ENETFEC_RCR 0x084 /* Receive control register */ +#define ENETFEC_TCR 0x0c4 /* Transmit Control register */ +#define ENETFEC_PALR 0x0e4 /* MAC address low 32 bits */ +#define ENETFEC_PAUR 0x0e8 /* MAC address high 16 bits */ +#define ENETFEC_OPD 0x0ec /* Opcode/Pause duration register */ +#define ENETFEC_IAUR 0x118 /* hash table 32 bits high */ +#define ENETFEC_IALR 0x11c /* hash table 32 bits low */ +#define ENETFEC_GAUR 0x120 /* grp hash table 32 bits high */ +#define ENETFEC_GALR 0x124 /* grp hash table 32 bits low */ +#define ENETFEC_TFWR 0x144 /* transmit FIFO water_mark */ +#define ENETFEC_RACC 0x1c4 /* Receive Accelerator function configuration*/ +#define ENETFEC_DMA1CFG 0x1d8 /* DMA class based configuration ring1 */ +#define ENETFEC_DMA2CFG 0x1dc /* DMA class based Configuration ring2 */ +#define ENETFEC_RDAR_1 0x1e0 /* Rx descriptor active register ring1 */ +#define ENETFEC_TDAR_1 0x1e4 /* Tx descriptor active register ring1 */ +#define ENETFEC_RDAR_2 0x1e8 /* Rx descriptor active register ring2 */ +#define ENETFEC_TDAR_2 0x1ec /* Tx descriptor active register ring2 */ +#define ENETFEC_RD_START_1 0x160 /* Receive descriptor ring1 start reg */ +#define ENETFEC_TD_START_1 0x164 /* Transmit descriptor ring1 start reg */ +#define ENETFEC_MRB_SIZE_1 0x168 /* Max receive buffer size reg ring1 */ +#define ENETFEC_RD_START_2 0x16c /* Receive descriptor ring2 start reg */ +#define ENETFEC_TD_START_2 0x170 /* Transmit descriptor ring2 start reg */ +#define ENETFEC_MRB_SIZE_2 0x174 /* Max receive buffer size reg ring2 */ +#define ENETFEC_RD_START_0 0x180 /* Receive descriptor ring0 start reg */ +#define ENETFEC_TD_START_0 0x184 /* Transmit descriptor ring0 start reg */ +#define ENETFEC_MRB_SIZE_0 0x188 /* Max receive buffer size reg ring0*/ +#define ENETFEC_R_FIFO_SFL 0x190 /* Rx FIFO full threshold */ +#define ENETFEC_R_FIFO_SEM 0x194 /* Rx FIFO empty threshold */ +#define ENETFEC_R_FIFO_AEM 0x198 /* Rx FIFO almost empty threshold */ +#define ENETFEC_R_FIFO_AFL 0x19c /* Rx FIFO almost full threshold */ +#define ENETFEC_FRAME_TRL 0x1b0 /* Frame truncation length */ + +#endif /*__ENETFEC_REGS_H */ diff --git a/drivers/net/enetfec/enet_uio.c b/drivers/net/enetfec/enet_uio.c new file mode 100644 index 0000000000..6539cbb354 --- /dev/null +++ b/drivers/net/enetfec/enet_uio.c @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2021 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "enet_pmd_logs.h" +#include "enet_uio.h" + +static struct uio_job enetfec_uio_job; +static int enetfec_count; + +/** @brief Checks if a file name contains a certain substring. + * This function assumes a filename format of: [text][number]. + * @param [in] filename File name + * @param [in] match String to match in file name + * + * @retval true if file name matches the criteria + * @retval false if file name does not match the criteria + */ +static bool +file_name_match_extract(const char filename[], const char match[]) +{ + char *substr = NULL; + + substr = strstr(filename, match); + if (substr == NULL) + return false; + + return true; +} + +/* + * @brief Reads first line from a file. + * Composes file name as: root/subdir/filename + * + * @param [in] root Root path + * @param [in] subdir Subdirectory name + * @param [in] filename File name + * @param [out] line The first line read from file. + * + * @retval 0 for success + * @retval other value for error + */ +static int +file_read_first_line(const char root[], const char subdir[], + const char filename[], char *line) +{ + char absolute_file_name[FEC_UIO_MAX_ATTR_FILE_NAME]; + int fd = 0, ret = 0; + + /*compose the file name: root/subdir/filename */ + memset(absolute_file_name, 0, sizeof(absolute_file_name)); + snprintf(absolute_file_name, FEC_UIO_MAX_ATTR_FILE_NAME, + "%s/%s/%s", root, subdir, filename); + + fd = open(absolute_file_name, O_RDONLY); + if (fd <= 0) + ENETFEC_PMD_ERR("Error opening file %s", absolute_file_name); + + /* read UIO device name from first line in file */ + ret = read(fd, line, FEC_UIO_MAX_DEVICE_FILE_NAME_LENGTH); + if (ret <= 0) { + ENETFEC_PMD_ERR("Error reading file %s", absolute_file_name); + return ret; + } + close(fd); + + /* NULL-ify string */ + line[ret] = '\0'; + + return 0; +} + +/* + * @brief Maps rx-tx bd range assigned for a bd ring. + * + * @param [in] uio_device_fd UIO device file descriptor + * @param [in] uio_device_id UIO device id + * @param [in] uio_map_id UIO allows maximum 5 different mapping for + each device. Maps start with id 0. + * @param [out] map_size Map size. + * @param [out] map_addr Map physical address + * + * @retval NULL if failed to map registers + * @retval Virtual address for mapped register address range + */ +static void * +uio_map_mem(int uio_device_fd, int uio_device_id, + int uio_map_id, int *map_size, uint64_t *map_addr) +{ + void *mapped_address = NULL; + unsigned int uio_map_size = 0; + unsigned int uio_map_p_addr = 0; + char uio_sys_root[FEC_UIO_MAX_ATTR_FILE_NAME]; + char uio_sys_map_subdir[FEC_UIO_MAX_ATTR_FILE_NAME]; + char uio_map_size_str[FEC_UIO_MAX_DEVICE_FILE_NAME_LENGTH + 1]; + char uio_map_p_addr_str[32]; + int ret = 0; + + /* compose the file name: root/subdir/filename */ + memset(uio_sys_root, 0, sizeof(uio_sys_root)); + memset(uio_sys_map_subdir, 0, sizeof(uio_sys_map_subdir)); + memset(uio_map_size_str, 0, sizeof(uio_map_size_str)); + memset(uio_map_p_addr_str, 0, sizeof(uio_map_p_addr_str)); + + /* Compose string: /sys/class/uio/uioX */ + snprintf(uio_sys_root, sizeof(uio_sys_root), "%s/%s%d", + FEC_UIO_DEVICE_SYS_ATTR_PATH, "uio", uio_device_id); + /* Compose string: maps/mapY */ + snprintf(uio_sys_map_subdir, sizeof(uio_sys_map_subdir), "%s%d", + FEC_UIO_DEVICE_SYS_MAP_ATTR, uio_map_id); + + /* Read first (and only) line from file + * /sys/class/uio/uioX/maps/mapY/size + */ + ret = file_read_first_line(uio_sys_root, uio_sys_map_subdir, + "size", uio_map_size_str); + if (ret < 0) { + ENETFEC_PMD_ERR("file_read_first_line() failed"); + return NULL; + } + ret = file_read_first_line(uio_sys_root, uio_sys_map_subdir, + "addr", uio_map_p_addr_str); + if (ret < 0) { + ENETFEC_PMD_ERR("file_read_first_line() failed"); + return NULL; + } + /* Read mapping size and physical address expressed in hexa(base 16) */ + uio_map_size = strtol(uio_map_size_str, NULL, 16); + uio_map_p_addr = strtol(uio_map_p_addr_str, NULL, 16); + + if (uio_map_id == 0) { + /* Map the register address in user space when map_id is 0 */ + mapped_address = mmap(0 /*dynamically choose virtual address */, + uio_map_size, PROT_READ | PROT_WRITE, + MAP_SHARED, uio_device_fd, 0); + } else { + /* Map the BD memory in user space */ + mapped_address = mmap(NULL, uio_map_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, uio_device_fd, (1 * MAP_PAGE_SIZE)); + } + + if (mapped_address == MAP_FAILED) { + ENETFEC_PMD_ERR("Failed to map! errno = %d uio job fd = %d," + "uio device id = %d, uio map id = %d", errno, + uio_device_fd, uio_device_id, uio_map_id); + return NULL; + } + + /* Save the map size to use it later on for munmap-ing */ + *map_size = uio_map_size; + *map_addr = uio_map_p_addr; + ENETFEC_PMD_INFO("UIO dev[%d] mapped region [id =%d] size 0x%x at %p", + uio_device_id, uio_map_id, uio_map_size, mapped_address); + + return mapped_address; +} + +int +config_enetfec_uio(struct enetfec_private *fep) +{ + char uio_device_file_name[32]; + struct uio_job *uio_job = NULL; + + /* Mapping is done only one time */ + if (enetfec_count > 0) { + ENETFEC_PMD_INFO("Mapped!\n"); + return 0; + } + + uio_job = &enetfec_uio_job; + + /* Find UIO device created by ENETFEC-UIO kernel driver */ + memset(uio_device_file_name, 0, sizeof(uio_device_file_name)); + snprintf(uio_device_file_name, sizeof(uio_device_file_name), "%s%d", + FEC_UIO_DEVICE_FILE_NAME, uio_job->uio_minor_number); + + /* Open device file */ + uio_job->uio_fd = open(uio_device_file_name, O_RDWR); + if (uio_job->uio_fd < 0) { + ENETFEC_PMD_WARN("Unable to open ENETFEC_UIO file\n"); + return -1; + } + + ENETFEC_PMD_INFO("US_UIO: Open device(%s) file with uio_fd = %d", + uio_device_file_name, uio_job->uio_fd); + + fep->hw_baseaddr_v = uio_map_mem(uio_job->uio_fd, + uio_job->uio_minor_number, FEC_UIO_REG_MAP_ID, + &uio_job->map_size, &uio_job->map_addr); + if (fep->hw_baseaddr_v == NULL) + return -ENOMEM; + fep->hw_baseaddr_p = uio_job->map_addr; + fep->reg_size = uio_job->map_size; + + fep->bd_addr_v = uio_map_mem(uio_job->uio_fd, + uio_job->uio_minor_number, FEC_UIO_BD_MAP_ID, + &uio_job->map_size, &uio_job->map_addr); + if (fep->hw_baseaddr_v == NULL) + return -ENOMEM; + fep->bd_addr_p = (uint32_t)uio_job->map_addr; + fep->bd_size = uio_job->map_size; + + enetfec_count++; + + return 0; +} + +int +enetfec_configure(void) +{ + char uio_name[32]; + int uio_minor_number = -1; + int ret; + DIR *d = NULL; + struct dirent *dir; + + d = opendir(FEC_UIO_DEVICE_SYS_ATTR_PATH); + if (d == NULL) { + ENETFEC_PMD_ERR("\nError opening directory '%s': %s\n", + FEC_UIO_DEVICE_SYS_ATTR_PATH, strerror(errno)); + return -1; + } + + /* Iterate through all subdirs */ + while ((dir = readdir(d)) != NULL) { + if (!strncmp(dir->d_name, ".", 1) || + !strncmp(dir->d_name, "..", 2)) + continue; + + if (file_name_match_extract(dir->d_name, "uio")) { + /* + * As substring was found in + * read number following substring in + */ + ret = sscanf(dir->d_name + strlen("uio"), "%d", + &uio_minor_number); + if (ret < 0) + ENETFEC_PMD_ERR("Error: not find minor number\n"); + /* + * Open file uioX/name and read first line which + * contains the name for the device. Based on the + * name check if this UIO device is for enetfec. + */ + memset(uio_name, 0, sizeof(uio_name)); + ret = file_read_first_line(FEC_UIO_DEVICE_SYS_ATTR_PATH, + dir->d_name, "name", uio_name); + if (ret != 0) { + ENETFEC_PMD_INFO("file_read_first_line failed\n"); + closedir(d); + return -1; + } + + if (file_name_match_extract(uio_name, + FEC_UIO_DEVICE_NAME)) { + enetfec_uio_job.uio_minor_number = + uio_minor_number; + ENETFEC_PMD_INFO("enetfec device uio name: %s", + uio_name); + } + } + } + closedir(d); + return 0; +} + +void +enetfec_cleanup(struct enetfec_private *fep) +{ + munmap(fep->hw_baseaddr_v, fep->cbus_size); +} diff --git a/drivers/net/enetfec/enet_uio.h b/drivers/net/enetfec/enet_uio.h new file mode 100644 index 0000000000..fec8ba6f95 --- /dev/null +++ b/drivers/net/enetfec/enet_uio.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2021 NXP + */ + +#include "enet_ethdev.h" + +/* Prefix path to sysfs directory where UIO device attributes are exported. + * Path for UIO device X is /sys/class/uio/uioX + */ +#define FEC_UIO_DEVICE_SYS_ATTR_PATH "/sys/class/uio" + +/* Subfolder in sysfs where mapping attributes are exported + * for each UIO device. Path for mapping Y for device X is: + * /sys/class/uio/uioX/maps/mapY + */ +#define FEC_UIO_DEVICE_SYS_MAP_ATTR "maps/map" + +/* Name of UIO device file prefix. Each UIO device will have a device file + * /dev/uioX, where X is the minor device number. + */ +#define FEC_UIO_DEVICE_FILE_NAME "/dev/uio" +/* + * Name of UIO device. User space FEC will have a corresponding + * UIO device. + * Maximum length is #FEC_UIO_MAX_DEVICE_NAME_LENGTH. + * + * @note Must be kept in sync with FEC kernel driver + * define #FEC_UIO_DEVICE_NAME ! + */ +#define FEC_UIO_DEVICE_NAME "imx-fec-uio" + +/* Maximum length for the name of an UIO device file. + * Device file name format is: /dev/uioX. + */ +#define FEC_UIO_MAX_DEVICE_FILE_NAME_LENGTH 30 + +/* Maximum length for the name of an attribute file for an UIO device. + * Attribute files are exported in sysfs and have the name formatted as: + * /sys/class/uio/uioX/ + */ +#define FEC_UIO_MAX_ATTR_FILE_NAME 100 + +/* The id for the mapping used to export ENETFEC registers and BD memory to + * user space through UIO device. + */ +#define FEC_UIO_REG_MAP_ID 0 +#define FEC_UIO_BD_MAP_ID 1 + +#define MAP_PAGE_SIZE 4096 + +struct uio_job { + uint32_t fec_id; + int uio_fd; + void *bd_start_addr; + void *register_base_addr; + int map_size; + uint64_t map_addr; + int uio_minor_number; +}; + +int enetfec_configure(void); +int config_enetfec_uio(struct enetfec_private *fep); +void enetfec_uio_init(void); +void enetfec_cleanup(struct enetfec_private *fep); diff --git a/drivers/net/enetfec/meson.build b/drivers/net/enetfec/meson.build index 42ec41502b..3fb0f73071 100644 --- a/drivers/net/enetfec/meson.build +++ b/drivers/net/enetfec/meson.build @@ -7,4 +7,5 @@ if not is_linux endif sources = files( - 'enet_ethdev.c') + 'enet_ethdev.c', + 'enet_uio.c') -- 2.17.1