From: "Alejandro.Lucero" <alejandro.luc...@netronome.com>

Current Netronome's PMD just supports Virtual Functions. Future Physical
Function support will require specific Netronome code here.

Signed-off-by: Alejandro.Lucero <alejandro.lucero at netronome.com>
Signed-off-by: Rolf.Neugebauer <rolf.neugebauer at netronome.com>
---
 lib/librte_eal/common/include/rte_pci.h   |    1 +
 lib/librte_eal/linuxapp/eal/eal_pci.c     |    4 +
 lib/librte_eal/linuxapp/eal/eal_pci_uio.c |    2 +-
 lib/librte_eal/linuxapp/nfp_uio/Makefile  |   53 +++
 lib/librte_eal/linuxapp/nfp_uio/nfp_uio.c |  497 +++++++++++++++++++++++++++++
 lib/librte_ether/rte_ethdev.c             |    1 +
 6 files changed, 557 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/linuxapp/nfp_uio/Makefile
 create mode 100644 lib/librte_eal/linuxapp/nfp_uio/nfp_uio.c

diff --git a/lib/librte_eal/common/include/rte_pci.h 
b/lib/librte_eal/common/include/rte_pci.h
index 83e3c28..89baaf6 100644
--- a/lib/librte_eal/common/include/rte_pci.h
+++ b/lib/librte_eal/common/include/rte_pci.h
@@ -146,6 +146,7 @@ struct rte_devargs;
 enum rte_kernel_driver {
        RTE_KDRV_UNKNOWN = 0,
        RTE_KDRV_IGB_UIO,
+       RTE_KDRV_NFP_UIO,
        RTE_KDRV_VFIO,
        RTE_KDRV_UIO_GENERIC,
        RTE_KDRV_NIC_UIO,
diff --git a/lib/librte_eal/linuxapp/eal/eal_pci.c 
b/lib/librte_eal/linuxapp/eal/eal_pci.c
index bc5b5be..19a93fe 100644
--- a/lib/librte_eal/linuxapp/eal/eal_pci.c
+++ b/lib/librte_eal/linuxapp/eal/eal_pci.c
@@ -137,6 +137,7 @@ pci_map_device(struct rte_pci_device *dev)
 #endif
                break;
        case RTE_KDRV_IGB_UIO:
+       case RTE_KDRV_NFP_UIO:
        case RTE_KDRV_UIO_GENERIC:
                /* map resources for devices that use uio */
                ret = pci_uio_map_resource(dev);
@@ -161,6 +162,7 @@ pci_unmap_device(struct rte_pci_device *dev)
                RTE_LOG(ERR, EAL, "Hotplug doesn't support vfio yet\n");
                break;
        case RTE_KDRV_IGB_UIO:
+       case RTE_KDRV_NFP_UIO:
        case RTE_KDRV_UIO_GENERIC:
                /* unmap resources for devices that use uio */
                pci_uio_unmap_resource(dev);
@@ -357,6 +359,8 @@ pci_scan_one(const char *dirname, uint16_t domain, uint8_t 
bus,
                        dev->kdrv = RTE_KDRV_VFIO;
                else if (!strcmp(driver, "igb_uio"))
                        dev->kdrv = RTE_KDRV_IGB_UIO;
+               else if (!strcmp(driver, "nfp_uio"))
+                       dev->kdrv = RTE_KDRV_NFP_UIO;
                else if (!strcmp(driver, "uio_pci_generic"))
                        dev->kdrv = RTE_KDRV_UIO_GENERIC;
                else
diff --git a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c 
b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
index ac50e13..29ec9cb 100644
--- a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
+++ b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
@@ -270,7 +270,7 @@ pci_uio_alloc_resource(struct rte_pci_device *dev,
                goto error;
        }

-       if (dev->kdrv == RTE_KDRV_IGB_UIO)
+       if (dev->kdrv == RTE_KDRV_IGB_UIO || dev->kdrv == RTE_KDRV_NFP_UIO)
                dev->intr_handle.type = RTE_INTR_HANDLE_UIO;
        else {
                dev->intr_handle.type = RTE_INTR_HANDLE_UIO_INTX;
diff --git a/lib/librte_eal/linuxapp/nfp_uio/Makefile 
b/lib/librte_eal/linuxapp/nfp_uio/Makefile
new file mode 100644
index 0000000..b9e2f0a
--- /dev/null
+++ b/lib/librte_eal/linuxapp/nfp_uio/Makefile
@@ -0,0 +1,53 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2014-2015 Netronome. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+#
+# module name and path
+#
+MODULE = nfp_uio
+MODULE_PATH = drivers/net/nfp_uio
+
+#
+# CFLAGS
+#
+MODULE_CFLAGS += -I$(SRCDIR) --param max-inline-insns-single=100
+MODULE_CFLAGS += -I$(RTE_OUTPUT)/include
+MODULE_CFLAGS += -Winline -Wall -Werror
+MODULE_CFLAGS += -include $(RTE_OUTPUT)/include/rte_config.h
+
+#
+# all source are stored in SRCS-y
+#
+SRCS-y := nfp_uio.c
+
+include $(RTE_SDK)/mk/rte.module.mk
diff --git a/lib/librte_eal/linuxapp/nfp_uio/nfp_uio.c 
b/lib/librte_eal/linuxapp/nfp_uio/nfp_uio.c
new file mode 100644
index 0000000..98192a5
--- /dev/null
+++ b/lib/librte_eal/linuxapp/nfp_uio/nfp_uio.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 2014, 2015 Netronome Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *  contributors may be used to endorse or promote products derived from this
+ *  software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Netronome DPDK uio kernel module
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/uio_driver.h>
+#include <linux/io.h>
+#include <linux/msi.h>
+#include <linux/version.h>
+
+#ifndef PCI_MSIX_ENTRY_SIZE
+#define PCI_MSIX_ENTRY_SIZE             16
+#define PCI_MSIX_ENTRY_LOWER_ADDR       0
+#define PCI_MSIX_ENTRY_UPPER_ADDR       4
+#define PCI_MSIX_ENTRY_DATA             8
+#define PCI_MSIX_ENTRY_VECTOR_CTRL      12
+#define PCI_MSIX_ENTRY_CTRL_MASKBIT     1
+#endif
+
+/* Ideally we should support two types of interrupts:
+ *
+ * - Link Status Change Interrupt
+ * - Exception Interrupt
+ *
+ * But the uio Linux kernel interface just admits one interrupt per uio device.
+ */
+#define NFP_NUM_MSI_VECTORS 1
+
+/*
+ * A structure describing the private information for a uio device.
+ */
+struct nfp_uio_pci_dev {
+       struct uio_info info;
+       struct pci_dev *pdev;
+       /* spinlock for accessing PCI config space or msix
+        * data in multi tasks/isr
+        */
+       spinlock_t lock;
+
+       /* pointer to the msix vectors to be allocated later */
+       struct msix_entry msix_entries[NFP_NUM_MSI_VECTORS];
+};
+
+#define PCI_VENDOR_ID_NETRONOME                0x19ee
+#define PCI_DEVICE_NFP6000_VF_NIC      0x6003
+
+#define RTE_PCI_DEV_ID_DECL_NETRO(vend, dev) {PCI_DEVICE(vend, dev)},
+
+/* PCI device id table */
+static struct pci_device_id nfp_uio_pci_ids[] = {
+RTE_PCI_DEV_ID_DECL_NETRO(PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_NFP6000_VF_NIC)
+{ 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, nfp_uio_pci_ids);
+
+static inline struct nfp_uio_pci_dev *
+nfp_uio_get_uio_pci_dev(struct uio_info *info)
+{
+       return container_of(info, struct nfp_uio_pci_dev, info);
+}
+
+static inline int
+pci_lock(struct pci_dev *pdev)
+{
+       /* Some function names changes between 3.2.0 and 3.3.0... */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+       pci_block_user_cfg_access(pdev);
+       return 1;
+#else
+       return pci_cfg_access_trylock(pdev);
+#endif
+}
+
+static inline void
+pci_unlock(struct pci_dev *pdev)
+{
+       /* Some function names changes between 3.2.0 and 3.3.0... */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+       pci_unblock_user_cfg_access(pdev);
+#else
+       pci_cfg_access_unlock(pdev);
+#endif
+}
+
+/*
+ * It masks the msix on/off of generating MSI-X messages.
+ */
+static int
+nfp_uio_msix_mask_irq(struct msi_desc *desc, int32_t state)
+{
+       u32 mask_bits = desc->masked;
+       unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
+                                               PCI_MSIX_ENTRY_VECTOR_CTRL;
+
+       if (state != 0)
+               mask_bits &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
+       else
+               mask_bits |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
+
+       if (mask_bits != desc->masked) {
+               writel(mask_bits, desc->mask_base + offset);
+               readl(desc->mask_base);
+               desc->masked = mask_bits;
+       }
+
+       return 0;
+}
+
+/**
+ * This function sets/clears the masks for generating LSC interrupts.
+ *
+ * @param info
+ *   The pointer to struct uio_info.
+ * @param on
+ *   The on/off flag of masking LSC.
+ * @return
+ *   -On success, zero value.
+ *   -On failure, a negative value.
+ */
+static int
+nfp_uio_set_interrupt_mask(struct nfp_uio_pci_dev *udev, int32_t state)
+{
+       struct pci_dev *pdev = udev->pdev;
+       struct msi_desc *desc;
+
+       /* TODO: Should we change this based on if the firmware advertises
+                        NFP_NET_CFG_CTRL_MSIXAUTO? */
+
+       list_for_each_entry(desc, &pdev->msi_list, list) {
+               nfp_uio_msix_mask_irq(desc, state);
+       }
+       return 0;
+}
+
+/**
+ * This is the irqcontrol callback to be registered to uio_info.
+ * It can be used to disable/enable interrupt from user space processes.
+ *
+ * @param info
+ *  pointer to uio_info.
+ * @param irq_state
+ *  state value. 1 to enable interrupt, 0 to disable interrupt.
+ *
+ * @return
+ *  - On success, 0.
+ *  - On failure, a negative value.
+ */
+static int
+nfp_uio_pci_irqcontrol(struct uio_info *info, s32 irq_state)
+{
+       unsigned long flags;
+       struct nfp_uio_pci_dev *udev = nfp_uio_get_uio_pci_dev(info);
+       struct pci_dev *pdev = udev->pdev;
+
+       spin_lock_irqsave(&udev->lock, flags);
+       if (!pci_lock(pdev)) {
+               spin_unlock_irqrestore(&udev->lock, flags);
+               return -1;
+       }
+
+       nfp_uio_set_interrupt_mask(udev, irq_state);
+
+       pci_unlock(pdev);
+       spin_unlock_irqrestore(&udev->lock, flags);
+
+       return 0;
+}
+
+/**
+ * This is interrupt handler which will check if the interrupt is for the right
+   device. If yes, disable it here and will be enable later.
+ */
+static irqreturn_t
+nfp_uio_pci_irqhandler(int irq, struct uio_info *info)
+{
+       irqreturn_t ret = IRQ_NONE;
+       unsigned long flags;
+       struct nfp_uio_pci_dev *udev = nfp_uio_get_uio_pci_dev(info);
+       struct pci_dev *pdev = udev->pdev;
+
+       spin_lock_irqsave(&udev->lock, flags);
+       /* block userspace PCI config reads/writes */
+       if (!pci_lock(pdev))
+               goto spin_unlock;
+
+       ret = IRQ_HANDLED;
+
+       /* unblock userspace PCI config reads/writes */
+       pci_unlock(pdev);
+spin_unlock:
+       spin_unlock_irqrestore(&udev->lock, flags);
+       dev_info(&pdev->dev, "irq 0x%x %s\n", irq,
+                (ret == IRQ_HANDLED) ? "handled" : "not handled");
+
+       return ret;
+}
+
+/* Remap pci resources described by bar #pci_bar in uio resource n. */
+static int
+nfp_uio_pci_setup_iomem(struct pci_dev *dev, struct uio_info *info,
+                       int n, int pci_bar, const char *name)
+{
+       unsigned long addr, len;
+       void *internal_addr;
+
+       if (ARRAY_SIZE(info->mem) <= n)
+               return -EINVAL;
+
+       addr = pci_resource_start(dev, pci_bar);
+       len = pci_resource_len(dev, pci_bar);
+       if (addr == 0 || len == 0)
+               return -1;
+       internal_addr = ioremap(addr, len);
+       if (!internal_addr)
+               return -1;
+       info->mem[n].name = name;
+       info->mem[n].addr = addr;
+       info->mem[n].internal_addr = internal_addr;
+       info->mem[n].size = len;
+       info->mem[n].memtype = UIO_MEM_PHYS;
+       return 0;
+}
+
+/* Get pci port io resources described by bar #pci_bar in uio resource n. */
+static int
+nfp_uio_pci_setup_ioport(struct pci_dev *dev, struct uio_info *info,
+                        int n, int pci_bar, const char *name)
+{
+       unsigned long addr, len;
+
+       if (ARRAY_SIZE(info->port) <= n)
+               return -EINVAL;
+
+       addr = pci_resource_start(dev, pci_bar);
+       len = pci_resource_len(dev, pci_bar);
+       if (addr == 0 || len == 0)
+               return -1;
+
+       info->port[n].name = name;
+       info->port[n].start = addr;
+       info->port[n].size = len;
+       info->port[n].porttype = UIO_PORT_X86;
+
+       return 0;
+}
+
+/* Unmap previously ioremap'd resources */
+static void
+nfp_uio_pci_release_iomem(struct uio_info *info)
+{
+       int i;
+
+       for (i = 0; i < MAX_UIO_MAPS; i++) {
+               if (info->mem[i].internal_addr)
+                       iounmap(info->mem[i].internal_addr);
+       }
+}
+
+static int
+nfp_uio_setup_bars(struct pci_dev *dev, struct uio_info *info)
+{
+       int i, iom, iop, ret;
+       unsigned long flags;
+       static const char *bar_names[PCI_STD_RESOURCE_END + 1]  = {
+               "BAR0",
+               "BAR1",
+               "BAR2",
+               "BAR3",
+               "BAR4",
+               "BAR5",
+       };
+
+       iom = 0;
+       iop = 0;
+
+       for (i = 0; i != ARRAY_SIZE(bar_names); i++) {
+               if (pci_resource_len(dev, i) == 0 ||
+                   pci_resource_start(dev, i) == 0)
+                       continue;
+
+               flags = pci_resource_flags(dev, i);
+               if (flags & IORESOURCE_MEM) {
+                       ret = nfp_uio_pci_setup_iomem(dev, info, iom, i,
+                                                     bar_names[i]);
+                       if (ret != 0)
+                               return ret;
+                       iom++;
+               } else if (flags & IORESOURCE_IO) {
+                       ret = nfp_uio_pci_setup_ioport(dev, info, iop, i,
+                                                      bar_names[i]);
+                       if (ret != 0)
+                               return ret;
+                       iop++;
+               }
+       }
+
+       return (iom != 0) ? ret : -ENOENT;
+}
+
+/* Configuring interrupt. First try MSI-X, then MSI. */
+static void
+init_interrupt(struct nfp_uio_pci_dev *udev)
+{
+       int vector;
+
+       for (vector = 0; vector < NFP_NUM_MSI_VECTORS; vector++)
+               udev->msix_entries[vector].entry = vector;
+
+       if (pci_enable_msix(udev->pdev, udev->msix_entries,
+                           NFP_NUM_MSI_VECTORS) == 0) {
+               udev->info.irq_flags = 0;
+               udev->info.irq = udev->msix_entries[0].vector;
+               dev_info(&udev->pdev->dev, "%s configured with MSI-X\n",
+                        udev->info.name);
+       } else
+               dev_info(&udev->pdev->dev, "%s MSI-X initialization error\n",
+                        udev->info.name);
+}
+
+static int
+nfp_uio_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+       struct nfp_uio_pci_dev *udev;
+       void *map_addr;
+       dma_addr_t map_dma_addr;
+
+       udev = kzalloc(sizeof(*udev), GFP_KERNEL);
+       if (!udev)
+               return -ENOMEM;
+
+       /*
+        * enable device: ask low-level code to enable I/O and
+        * memory
+        */
+       if (pci_enable_device(dev)) {
+               dev_err(&dev->dev, "Cannot enable PCI device\n");
+               goto fail_free;
+       }
+
+       /*
+        * reserve device's PCI memory regions for use by this
+        * module
+        */
+       if (pci_request_regions(dev, "nfp_uio")) {
+               dev_err(&dev->dev, "Cannot request regions\n");
+               goto fail_disable;
+       }
+
+       /* enable bus mastering on the device */
+       pci_set_master(dev);
+
+       /* remap IO memory */
+       if (nfp_uio_setup_bars(dev, &udev->info))
+               goto fail_release_iomem;
+
+       /* set 40-bit DMA mask */
+       if (pci_set_dma_mask(dev,  DMA_BIT_MASK(40))) {
+               dev_err(&dev->dev, "Cannot set DMA mask\n");
+               goto fail_release_iomem;
+       } else if (pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(40))) {
+               dev_err(&dev->dev, "Cannot set consistent DMA mask\n");
+               goto fail_release_iomem;
+       }
+
+       /* fill uio infos */
+       udev->info.name = "Netronome NFP UIO";
+       udev->info.version = "0.1";
+       udev->info.handler = nfp_uio_pci_irqhandler;
+       udev->info.irqcontrol = nfp_uio_pci_irqcontrol;
+       udev->info.priv = udev;
+       udev->pdev = dev;
+       spin_lock_init(&udev->lock);
+
+       init_interrupt(udev);
+
+       pci_set_drvdata(dev, &udev->info);
+       nfp_uio_pci_irqcontrol(&udev->info, 0);
+
+       /* register uio driver */
+       if (uio_register_device(&dev->dev, &udev->info))
+               goto fail_release_iomem;
+
+       dev_info(&dev->dev, "uio device registered with irq %lx\n",
+                udev->info.irq);
+
+       /* When binding drivers to devices, some old kernels do not
+        * link devices to iommu identity mapping if iommu=pt is used.
+        *
+        * This is not a problem if the driver does later some call to
+        * the DMA API because the mapping can be done then. But DPDK
+        * apps do not use that DMA API at all.
+        *
+        * Doing a harmless dma mapping for attaching the device to
+        * the iommu identity mapping
+        */
+
+       map_addr = dma_zalloc_coherent(&dev->dev, 1024,
+                                      &map_dma_addr, GFP_KERNEL);
+
+       pr_info("nfp_uio: mapping 1K dma=%#llx host=%p\n",
+               (unsigned long long)map_dma_addr, map_addr);
+
+       dma_free_coherent(&dev->dev, 1024, map_addr, map_dma_addr);
+
+       pr_info("nfp_uio: unmapping 1K dma=%#llx host=%p\n",
+               (unsigned long long)map_dma_addr, map_addr);
+
+       return 0;
+
+fail_release_iomem:
+       nfp_uio_pci_release_iomem(&udev->info);
+       pci_disable_msix(udev->pdev);
+       pci_release_regions(dev);
+fail_disable:
+       pci_disable_device(dev);
+fail_free:
+       kfree(udev);
+
+       return -ENODEV;
+}
+
+static void
+nfp_uio_pci_remove(struct pci_dev *dev)
+{
+       struct uio_info *info = pci_get_drvdata(dev);
+
+       BUG_ON(!info);
+       BUG_ON(!info->priv);
+
+       uio_unregister_device(info);
+       nfp_uio_pci_release_iomem(info);
+       pci_disable_msix(dev);
+       pci_release_regions(dev);
+       pci_disable_device(dev);
+       pci_set_drvdata(dev, NULL);
+       kfree(info);
+}
+
+static struct pci_driver nfp_uio_pci_driver = {
+       .name = "nfp_uio",
+       .id_table = nfp_uio_pci_ids,
+       .probe = nfp_uio_pci_probe,
+       .remove = nfp_uio_pci_remove,
+};
+
+static int __init
+nfp_uio_pci_init_module(void)
+{
+       return pci_register_driver(&nfp_uio_pci_driver);
+}
+
+static void __exit
+nfp_uio_pci_exit_module(void)
+{
+       pci_unregister_driver(&nfp_uio_pci_driver);
+}
+
+module_init(nfp_uio_pci_init_module);
+module_exit(nfp_uio_pci_exit_module);
+
+MODULE_DESCRIPTION("UIO driver for Netronome NFP PCI cards");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Netronome Systems <support at netronome.com>");
diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c
index f593f6e..a84bc63 100644
--- a/lib/librte_ether/rte_ethdev.c
+++ b/lib/librte_ether/rte_ethdev.c
@@ -513,6 +513,7 @@ rte_eth_dev_is_detachable(uint8_t port_id)
        if (rte_eth_devices[port_id].dev_type == RTE_ETH_DEV_PCI) {
                switch (rte_eth_devices[port_id].pci_dev->kdrv) {
                case RTE_KDRV_IGB_UIO:
+               case RTE_KDRV_NFP_UIO:
                case RTE_KDRV_UIO_GENERIC:
                case RTE_KDRV_NIC_UIO:
                        break;
-- 
1.7.9.5

Reply via email to