net/enetfec: support UIO
authorApeksha Gupta <apeksha.gupta@nxp.com>
Mon, 15 Nov 2021 07:19:37 +0000 (12:49 +0530)
committerFerruh Yigit <ferruh.yigit@intel.com>
Mon, 15 Nov 2021 15:26:17 +0000 (16:26 +0100)
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 <sachin.saxena@nxp.com>
Signed-off-by: Apeksha Gupta <apeksha.gupta@nxp.com>
Acked-by: Hemant Agrawal <hemant.agrawal@nxp.com>
Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
drivers/net/enetfec/enet_ethdev.c
drivers/net/enetfec/enet_ethdev.h
drivers/net/enetfec/enet_regs.h [new file with mode: 0644]
drivers/net/enetfec/enet_uio.c [new file with mode: 0644]
drivers/net/enetfec/enet_uio.h [new file with mode: 0644]
drivers/net/enetfec/meson.build

index 2cd503b..0d8d9f2 100644 (file)
 
 #include <ethdev_vdev.h>
 #include <ethdev_driver.h>
+#include <rte_io.h>
 #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;
 }
@@ -23,6 +201,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);
@@ -34,6 +214,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;
index 7e941da..4d671e6 100644 (file)
@@ -5,14 +5,45 @@
 #ifndef __ENETFEC_ETHDEV_H__
 #define __ENETFEC_ETHDEV_H__
 
+#include <rte_ethdev.h>
+
+/* 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 (file)
index 0000000..5415ed7
--- /dev/null
@@ -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 (file)
index 0000000..6539cbb
--- /dev/null
@@ -0,0 +1,284 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2021 NXP
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#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 <uio> was found in <d_name>
+                        * read number following <uio> substring in <d_name>
+                        */
+                       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 (file)
index 0000000..fec8ba6
--- /dev/null
@@ -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/<attribute_file_name>
+ */
+#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);
index cb5c402..b218d13 100644 (file)
@@ -8,4 +8,5 @@ endif
 
 sources = files(
         'enet_ethdev.c',
+        'enet_uio.c',
 )