diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index 6a08811..999c932 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -139,6 +139,7 @@ __LIB__libodp_la_SOURCES = \
 			   odp_packet.c \
 			   odp_packet_flags.c \
 			   odp_packet_io.c \
+			   odp_pci.c \
 			   odp_pci_vfio.c \
 			   pktio/io_ops.c \
 			   pktio/pktio_common.c \
diff --git a/platform/linux-generic/odp_pci.c b/platform/linux-generic/odp_pci.c
new file mode 100644
index 0000000..7cce69f
--- /dev/null
+++ b/platform/linux-generic/odp_pci.c
@@ -0,0 +1,190 @@
+/* Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include <stdio.h>
+#include <linux/limits.h>
+#include <stdlib.h>
+#include <odp/dma.h>
+#include <odp/pci.h>
+#include <odp_debug_internal.h>
+#include <odp_pci_internal.h>
+#include <odp_pci_vfio_internal.h>
+
+#define SYSFS_PCI_DEVICES "/sys/bus/pci/devices"
+
+/* operations on PCI devices: */
+
+uint16_t odp_pci_get_vendor(odp_pci_dev_t dev_hdl)
+{
+	return ((pci_dev_t *)dev_hdl)->pci_id.vendor_id;
+}
+
+uint16_t odp_pci_get_device(odp_pci_dev_t dev_hdl)
+{
+	return ((pci_dev_t *)dev_hdl)->pci_id.device_id;
+}
+
+uint16_t odp_pci_get_subsystem_vendor(odp_pci_dev_t dev_hdl)
+{
+	return ((pci_dev_t *)dev_hdl)->pci_id.subsystem_vendor_id;
+}
+
+uint16_t odp_pci_get_subsystem_device(odp_pci_dev_t dev_hdl)
+{
+	return ((pci_dev_t *)dev_hdl)->pci_id.subsystem_device_id;
+}
+
+void *odp_pci_get_resource_addr(odp_pci_dev_t dev_hdl, int bar)
+{
+	return ((pci_dev_t *)dev_hdl)->bar_maps[bar].addr;
+}
+
+char *odp_pci_get_addr_str(odp_pci_dev_t dev_hdl)
+{
+	return ((pci_dev_t *)dev_hdl)->pci_address;
+}
+
+/*
+ * parse a sysfs (or other) file containing one integer value
+ */
+static int parse_sysfs_value(const char *filename, unsigned long *val)
+{
+	FILE *f;
+	char buf[BUFSIZ];
+	char *end = NULL;
+
+	f = fopen(filename, "r");
+	if (!f) {
+		ODP_ERR("%s(): cannot open sysfs value %s\n",
+			__func__, filename);
+		return -1;
+	}
+
+	if (fgets(buf, sizeof(buf), f) == NULL) {
+		ODP_ERR("%s(): cannot read sysfs value %s\n",
+			__func__, filename);
+		fclose(f);
+		return -1;
+	}
+	*val = strtoul(buf, &end, 0);
+	if ((buf[0] == '\0') || (end == NULL) || (*end != '\n')) {
+		ODP_ERR("%s(): cannot parse sysfs value %s\n",
+			__func__, filename);
+		fclose(f);
+		return -1;
+	}
+	fclose(f);
+	return 0;
+}
+
+odp_pci_dev_t _odp_pci_init(const char *pci_addr)
+{
+	char filename[PATH_MAX];
+	unsigned long tmp;
+	pci_dev_t *dev;
+
+	dev = malloc(sizeof(pci_dev_t));
+	if (!dev)
+		return NULL;
+
+	/* currentely only vfio is supported */
+	if (_odp_pci_vfio_init(pci_addr, dev) < 0)
+		goto pci_init_err;
+
+	/* get vendor id */
+	snprintf(filename, sizeof(filename), SYSFS_PCI_DEVICES "/%s/vendor",
+		 pci_addr);
+	if (parse_sysfs_value(filename, &tmp) < 0)
+		goto pci_init_err;
+
+	dev->pci_id.vendor_id = (uint16_t)tmp;
+
+	/* get device id */
+	snprintf(filename, sizeof(filename), SYSFS_PCI_DEVICES "/%s/device",
+		 pci_addr);
+	if (parse_sysfs_value(filename, &tmp) < 0)
+		goto pci_init_err;
+
+	dev->pci_id.device_id = (uint16_t)tmp;
+
+	/* get subsystem_vendor id */
+	snprintf(filename, sizeof(filename),
+		 SYSFS_PCI_DEVICES "/%s/subsystem_vendor",
+		 pci_addr);
+	if (parse_sysfs_value(filename, &tmp) < 0)
+		goto pci_init_err;
+
+	dev->pci_id.subsystem_vendor_id = (uint16_t)tmp;
+
+	/* get subsystem_device id */
+	snprintf(filename, sizeof(filename),
+		 SYSFS_PCI_DEVICES "/%s/subsystem_device",
+		 pci_addr);
+	if (parse_sysfs_value(filename, &tmp) < 0)
+		goto pci_init_err;
+
+	dev->pci_id.subsystem_device_id = (uint16_t)tmp;
+
+	return (odp_pci_dev_t)dev;
+
+pci_init_err:
+	free(dev);
+	return NULL;
+}
+
+void _odp_pci_release(odp_pci_dev_t pci_dev)
+{
+	/* currentely only vfio is supported */
+	_odp_pci_vfio_release((pci_dev_t *)pci_dev);
+	free(pci_dev);
+}
+
+int odp_pci_map_dma_region(odp_pci_dev_t dev_hdl,
+			   odp_dma_map_t region)
+{
+	pci_dev_t *dev = (pci_dev_t *)dev_hdl;
+	dma_map_t *map_s = dma_map_handle_to_map(region);
+
+	/* map fragments one by one */
+	/* currentely only vfio is supported */
+	do {
+		if (_odp_pci_vfio_map_dma_region_f(dev, map_s)) {
+			/* remove partially mapped region: */
+			odp_pci_unmap_dma_region(dev_hdl, region);
+			return -1;
+		}
+		map_s = map_s->next;
+	} while (map_s);
+
+	return 0;
+}
+
+int odp_pci_unmap_dma_region(odp_pci_dev_t dev_hdl,
+			     odp_dma_map_t region)
+{
+	pci_dev_t *dev = (pci_dev_t *)dev_hdl;
+	dma_map_t *map_s = dma_map_handle_to_map(region);
+
+	/* unmap fragments one by one */
+	/* currentely only vfio is supported */
+	do {
+		_odp_pci_vfio_unmap_dma_region_f(dev, map_s);
+		map_s = map_s->next;
+	} while (map_s);
+
+	return 0;
+}
+
+int odp_pci_read_config(odp_pci_dev_t dev, void *buf, size_t len, int offs)
+{
+	return _odp_pci_vfio_read_config((pci_dev_t *)dev, buf, len, offs);
+}
+
+int odp_pci_write_config(odp_pci_dev_t dev, const void *buf,
+			 size_t len, int offs)
+{
+	return _odp_pci_vfio_write_config((pci_dev_t *)dev, buf, len, offs);
+}
