diff --git a/drivers/common/sfc_efx/base/efx.h b/drivers/common/sfc_efx/base/efx.h
index 0d4f5e5e70..f6dd9db301 100644
--- a/drivers/common/sfc_efx/base/efx.h
+++ b/drivers/common/sfc_efx/base/efx.h
@@ -59,6 +59,19 @@ typedef enum efx_family_e {
 	EFX_FAMILY_NTYPES
 } efx_family_t;
 
+typedef enum efx_bar_type_e {
+	EFX_BAR_TYPE_MEM,
+	EFX_BAR_TYPE_IO
+} efx_bar_type_t;
+
+typedef struct efx_bar_region_s {
+	efx_bar_type_t		ebr_type;
+	int			ebr_index;
+	efsys_dma_addr_t	ebr_offset;
+	efsys_dma_addr_t	ebr_length;
+} efx_bar_region_t;
+
+/* The function is deprecated. It is used only if Riverhead is not supported. */
 LIBEFX_API
 extern	__checkReturn	efx_rc_t
 efx_family(
@@ -67,6 +80,26 @@ efx_family(
 	__out		efx_family_t *efp,
 	__out		unsigned int *membarp);
 
+#if EFSYS_OPT_PCI
+
+/* Determine EFX family and perform lookup of the function control window
+ *
+ * The function requires PCI config handle from which all memory bars can
+ * be accessed.
+ * A user of the API must be aware of memory bars indexes (not available
+ * on Windows).
+ */
+LIBEFX_API
+extern	__checkReturn	efx_rc_t
+efx_family_probe_bar(
+	__in		uint16_t venid,
+	__in		uint16_t devid,
+	__in		efsys_pci_config_t *espcp,
+	__out		efx_family_t *efp,
+	__out		efx_bar_region_t *ebrp);
+
+#endif /* EFSYS_OPT_PCI */
+
 
 #define	EFX_PCI_VENID_SFC			0x1924
 #define	EFX_PCI_VENID_XILINX			0x10EE
diff --git a/drivers/common/sfc_efx/base/efx_check.h b/drivers/common/sfc_efx/base/efx_check.h
index 978321cf67..af90a4c373 100644
--- a/drivers/common/sfc_efx/base/efx_check.h
+++ b/drivers/common/sfc_efx/base/efx_check.h
@@ -378,4 +378,10 @@
 # endif
 #endif /* EFSYS_OPT_EVB */
 
+#if EFSYS_OPT_PCI
+# if !EFSYS_OPT_RIVERHEAD
+#  error "PCI requires RIVERHEAD"
+# endif
+#endif /* EFSYS_OPT_PCI */
+
 #endif /* _SYS_EFX_CHECK_H */
diff --git a/drivers/common/sfc_efx/base/efx_impl.h b/drivers/common/sfc_efx/base/efx_impl.h
index b1457f361a..422f97c5ca 100644
--- a/drivers/common/sfc_efx/base/efx_impl.h
+++ b/drivers/common/sfc_efx/base/efx_impl.h
@@ -1558,6 +1558,67 @@ efx_mcdi_mac_stats(
 
 #endif	/* EFSYS_OPT_MAC_STATS */
 
+#if EFSYS_OPT_PCI
+
+/*
+ * Find the next extended capability in a PCI device's config space
+ * with specified capability id.
+ * Passing 0 offset makes the function search from the start.
+ * If search succeeds, found capability is in modified offset.
+ *
+ * Returns ENOENT if a capability is not found.
+ */
+LIBEFX_INTERNAL
+extern	__checkReturn			efx_rc_t
+efx_pci_config_find_next_ext_cap(
+	__in				efsys_pci_config_t *espcp,
+	__in				uint16_t cap_id,
+	__inout				size_t *offsetp);
+
+/*
+ * Get the next extended capability in a PCI device's config space.
+ * Passing 0 offset makes the function get the first capability.
+ * If search succeeds, the capability is in modified offset.
+ *
+ * Returns ENOENT if there is no next capability.
+ */
+LIBEFX_INTERNAL
+extern	__checkReturn			efx_rc_t
+efx_pci_config_next_ext_cap(
+	__in				efsys_pci_config_t *espcp,
+	__inout				size_t *offsetp);
+
+/*
+ * Find the next Xilinx capabilities table location by searching
+ * PCI extended capabilities.
+ *
+ * Returns ENOENT if a table location is not found.
+ */
+LIBEFX_INTERNAL
+extern	__checkReturn			efx_rc_t
+efx_pci_find_next_xilinx_cap_table(
+	__in				efsys_pci_config_t *espcp,
+	__inout				size_t *pci_cap_offsetp,
+	__out				unsigned int *xilinx_tbl_barp,
+	__out				efsys_dma_addr_t *xilinx_tbl_offsetp);
+
+/*
+ * Read a Xilinx extended PCI capability that gives the location
+ * of a Xilinx capabilities table.
+ *
+ * Returns ENOENT if the extended PCI capability does not contain
+ * Xilinx capabilities table locator.
+ */
+LIBEFX_INTERNAL
+extern	__checkReturn			efx_rc_t
+efx_pci_read_ext_cap_xilinx_table(
+	__in				efsys_pci_config_t *espcp,
+	__in				size_t cap_offset,
+	__out				unsigned int *barp,
+	__out				efsys_dma_addr_t *offsetp);
+
+#endif /* EFSYS_OPT_PCI */
+
 #ifdef	__cplusplus
 }
 #endif
diff --git a/drivers/common/sfc_efx/base/efx_nic.c b/drivers/common/sfc_efx/base/efx_nic.c
index 3dc287a095..dcf0987ebf 100644
--- a/drivers/common/sfc_efx/base/efx_nic.c
+++ b/drivers/common/sfc_efx/base/efx_nic.c
@@ -103,6 +103,47 @@ efx_family(
 	return (ENOTSUP);
 }
 
+#if EFSYS_OPT_PCI
+
+	__checkReturn	efx_rc_t
+efx_family_probe_bar(
+	__in		uint16_t venid,
+	__in		uint16_t devid,
+	__in		efsys_pci_config_t *espcp,
+	__out		efx_family_t *efp,
+	__out		efx_bar_region_t *ebrp)
+{
+	efx_rc_t rc;
+	unsigned int membar;
+
+	if (venid == EFX_PCI_VENID_XILINX) {
+		switch (devid) {
+#if EFSYS_OPT_RIVERHEAD
+		case EFX_PCI_DEVID_RIVERHEAD:
+		case EFX_PCI_DEVID_RIVERHEAD_VF:
+			rc = rhead_pci_nic_membar_lookup(espcp, ebrp);
+			if (rc == 0)
+				*efp = EFX_FAMILY_RIVERHEAD;
+
+			return (rc);
+#endif /* EFSYS_OPT_RIVERHEAD */
+		default:
+			break;
+		}
+	}
+
+	rc = efx_family(venid, devid, efp, &membar);
+	if (rc == 0) {
+		ebrp->ebr_type = EFX_BAR_TYPE_MEM;
+		ebrp->ebr_index = membar;
+		ebrp->ebr_offset = 0;
+		ebrp->ebr_length = 0;
+	}
+
+	return (rc);
+}
+
+#endif /* EFSYS_OPT_PCI */
 
 #if EFSYS_OPT_SIENA
 
diff --git a/drivers/common/sfc_efx/base/efx_pci.c b/drivers/common/sfc_efx/base/efx_pci.c
new file mode 100644
index 0000000000..8707220849
--- /dev/null
+++ b/drivers/common/sfc_efx/base/efx_pci.c
@@ -0,0 +1,302 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright(c) 2019-2020 Xilinx, Inc.
+ * Copyright(c) 2019 Solarflare Communications Inc.
+ */
+
+#include "efx.h"
+#include "efx_impl.h"
+
+#if EFSYS_OPT_PCI
+
+	__checkReturn			efx_rc_t
+efx_pci_config_next_ext_cap(
+	__in				efsys_pci_config_t *espcp,
+	__inout				size_t *offsetp)
+{
+	efx_dword_t hdr;
+	efx_rc_t rc = 0;
+	size_t next;
+
+	if (offsetp == NULL) {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	if (*offsetp == 0) {
+		*offsetp = ESE_GZ_PCI_BASE_CONFIG_SPACE_SIZE;
+	} else {
+		EFSYS_PCI_CONFIG_READD(espcp, *offsetp +
+				(EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
+				&hdr, &rc);
+		if (rc != 0) {
+			rc = EIO;
+			goto fail2;
+		}
+
+		next = EFX_DWORD_FIELD(hdr, ESF_GZ_PCI_EXPRESS_XCAP_NEXT);
+		if (next < ESE_GZ_PCI_BASE_CONFIG_SPACE_SIZE)
+			rc = ENOENT;
+		else
+			*offsetp = next;
+	}
+
+	/*
+	 * Returns 0 if the next capability is present otherwise ENOENT
+	 * indicating that the function finished correctly.
+	 */
+	return (rc);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn			efx_rc_t
+efx_pci_config_find_next_ext_cap(
+	__in				efsys_pci_config_t *espcp,
+	__in				uint16_t cap_id,
+	__inout				size_t *offsetp)
+{
+	efx_dword_t hdr;
+	size_t position;
+	efx_rc_t rc;
+
+	if (offsetp == NULL) {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	position = *offsetp;
+
+	while (1) {
+		rc = efx_pci_config_next_ext_cap(espcp, &position);
+		if (rc != 0) {
+			if (rc == ENOENT)
+				break;
+			else
+				goto fail2;
+		}
+
+		EFSYS_PCI_CONFIG_READD(espcp, position +
+				(EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
+				&hdr, &rc);
+		if (rc != 0) {
+			rc = EIO;
+			goto fail3;
+		}
+
+		if (EFX_DWORD_FIELD(hdr, ESF_GZ_PCI_EXPRESS_XCAP_ID) ==
+		    cap_id) {
+			*offsetp = position;
+			rc = 0;
+			break;
+		}
+	}
+
+	/*
+	 * Returns 0 if found otherwise ENOENT indicating that search finished
+	 * correctly.
+	 */
+	return (rc);
+
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn			efx_rc_t
+efx_pci_find_next_xilinx_cap_table(
+	__in				efsys_pci_config_t *espcp,
+	__inout				size_t *pci_cap_offsetp,
+	__out				unsigned int *xilinx_tbl_barp,
+	__out				efsys_dma_addr_t *xilinx_tbl_offsetp)
+{
+	size_t cap_offset;
+	efx_rc_t rc;
+
+	if (pci_cap_offsetp == NULL) {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	cap_offset = *pci_cap_offsetp;
+
+	while (1) {
+		unsigned int tbl_bar;
+		efsys_dma_addr_t tbl_offset;
+
+		rc = efx_pci_config_find_next_ext_cap(espcp,
+				ESE_GZ_PCI_EXPRESS_XCAP_ID_VNDR, &cap_offset);
+		if (rc != 0) {
+			if (rc == ENOENT)
+				break;
+			else
+				goto fail2;
+		}
+
+		/*
+		 * The found extended PCI capability is a vendor-specific
+		 * capability, but not necessarily a Xilinx capabilities table
+		 * locator. Try to read it and skip it if the capability is
+		 * not the locator.
+		 */
+		rc = efx_pci_read_ext_cap_xilinx_table(espcp, cap_offset,
+						       &tbl_bar, &tbl_offset);
+		if (rc == 0) {
+			*xilinx_tbl_barp = tbl_bar;
+			*xilinx_tbl_offsetp = tbl_offset;
+			*pci_cap_offsetp = cap_offset;
+			break;
+		} else {
+			if (rc == ENOENT)
+				continue;
+			else
+				goto fail3;
+		}
+	}
+
+	/*
+	 * Returns 0 if found otherwise ENOENT indicating that search finished
+	 * correctly.
+	 */
+	return (rc);
+
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn			efx_rc_t
+efx_pci_read_ext_cap_xilinx_table(
+	__in				efsys_pci_config_t *espcp,
+	__in				size_t cap_offset,
+	__out				unsigned int *barp,
+	__out				efsys_dma_addr_t *offsetp)
+{
+	size_t vsec_offset = cap_offset + ESE_GZ_PCI_EXPRESS_XCAP_HDR_SIZE;
+	efx_dword_t cap_hdr;
+	efx_oword_t vsec;
+	uint32_t vsec_len;
+	uint32_t vsec_id;
+	uint32_t vsec_rev;
+	uint32_t offset_low;
+	uint32_t offset_high = 0;
+	unsigned int bar;
+	efsys_dma_addr_t offset;
+	efx_rc_t rc;
+
+	EFSYS_PCI_CONFIG_READD(espcp, cap_offset +
+			       (EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
+			       &cap_hdr, &rc);
+	if (rc != 0) {
+		rc = EIO;
+		goto fail1;
+	}
+
+	if (EFX_DWORD_FIELD(cap_hdr, ESF_GZ_PCI_EXPRESS_XCAP_VER) !=
+	    ESE_GZ_PCI_EXPRESS_XCAP_VER_VSEC) {
+		rc = EINVAL;
+		goto fail2;
+	}
+
+	EFSYS_PCI_CONFIG_READD(espcp, vsec_offset +
+			       (EFX_LOW_BIT(ESF_GZ_VSEC_ID) / 8),
+			       &vsec.eo_dword[0], &rc);
+	if (rc != 0) {
+		rc = EIO;
+		goto fail3;
+	}
+
+	vsec_len = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_LEN);
+	vsec_id = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_ID);
+	vsec_rev = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_VER);
+
+	/*
+	 * Condition of the vendor-specific extended PCI capability not being
+	 * a Xilinx capabilities table locator.
+	 */
+	if (vsec_id != ESE_GZ_XILINX_VSEC_ID) {
+		rc = ENOENT;
+		goto fail4;
+	}
+
+	if (vsec_rev != ESE_GZ_VSEC_VER_XIL_CFGBAR ||
+	    vsec_len < ESE_GZ_VSEC_LEN_MIN) {
+		rc = EINVAL;
+		goto fail5;
+	}
+
+	EFSYS_PCI_CONFIG_READD(espcp, vsec_offset +
+			       (EFX_LOW_BIT(ESF_GZ_VSEC_TBL_BAR) / 8),
+			       &vsec.eo_dword[1], &rc);
+	if (rc != 0) {
+		rc = EIO;
+		goto fail6;
+	}
+
+	bar = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_TBL_BAR);
+	offset_low = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_TBL_OFF_LO);
+
+	if (vsec_len >= ESE_GZ_VSEC_LEN_HIGH_OFFT) {
+		EFSYS_PCI_CONFIG_READD(espcp, vsec_offset +
+				(EFX_LOW_BIT(ESF_GZ_VSEC_TBL_OFF_HI) / 8),
+				&vsec.eo_dword[2], &rc);
+		if (rc != 0) {
+			rc = EIO;
+			goto fail7;
+		}
+
+		offset_high = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_TBL_OFF_HI);
+	}
+
+	/* High bits of low offset are discarded by the shift */
+	offset = offset_low << ESE_GZ_VSEC_TBL_OFF_LO_BYTES_SHIFT;
+
+	/*
+	 * Avoid the 'left shift count >= width of type' warning on systems
+	 * without uint64_t support.
+	 */
+#if EFSYS_HAS_UINT64
+	offset |= (uint64_t)offset_high << ESE_GZ_VSEC_TBL_OFF_HI_BYTES_SHIFT;
+#else
+	_NOTE(ARGUNUSED(offset_high))
+#endif
+
+	*offsetp = offset;
+	*barp = bar;
+
+	return (0);
+
+fail7:
+	EFSYS_PROBE(fail7);
+fail6:
+	EFSYS_PROBE(fail6);
+fail5:
+	EFSYS_PROBE(fail5);
+fail4:
+	EFSYS_PROBE(fail4);
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+#endif /* EFSYS_OPT_PCI */
diff --git a/drivers/common/sfc_efx/base/meson.build b/drivers/common/sfc_efx/base/meson.build
index 8f944bb45b..21feb36c73 100644
--- a/drivers/common/sfc_efx/base/meson.build
+++ b/drivers/common/sfc_efx/base/meson.build
@@ -19,6 +19,7 @@ sources = [
 	'efx_mon.c',
 	'efx_nic.c',
 	'efx_nvram.c',
+	'efx_pci.c',
 	'efx_phy.c',
 	'efx_port.c',
 	'efx_proxy.c',
@@ -55,6 +56,7 @@ sources = [
 	'rhead_ev.c',
 	'rhead_intr.c',
 	'rhead_nic.c',
+	'rhead_pci.c',
 	'rhead_rx.c',
 	'rhead_tx.c',
 ]
diff --git a/drivers/common/sfc_efx/base/rhead_impl.h b/drivers/common/sfc_efx/base/rhead_impl.h
index fa5e2b4915..777179fb17 100644
--- a/drivers/common/sfc_efx/base/rhead_impl.h
+++ b/drivers/common/sfc_efx/base/rhead_impl.h
@@ -437,6 +437,19 @@ rhead_tx_qstats_update(
 
 #endif /* EFSYS_OPT_QSTATS */
 
+#if EFSYS_OPT_PCI
+
+/*
+ * Perform discovery of function control window by looking for a
+ * EF100 locator in Xilinx capabilities tables.
+ */
+LIBEFX_INTERNAL
+extern	__checkReturn			efx_rc_t
+rhead_pci_nic_membar_lookup(
+	__in				efsys_pci_config_t *espcp,
+	__out				efx_bar_region_t *ebrp);
+
+#endif /* EFSYS_OPT_PCI */
 
 #ifdef	__cplusplus
 }
diff --git a/drivers/common/sfc_efx/base/rhead_pci.c b/drivers/common/sfc_efx/base/rhead_pci.c
new file mode 100644
index 0000000000..f8e372b79c
--- /dev/null
+++ b/drivers/common/sfc_efx/base/rhead_pci.c
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright(c) 2019-2020 Xilinx, Inc.
+ * Copyright(c) 2019 Solarflare Communications Inc.
+ */
+
+#include "efx.h"
+#include "efx_impl.h"
+
+#if EFSYS_OPT_RIVERHEAD && EFSYS_OPT_PCI
+
+	__checkReturn			efx_rc_t
+rhead_pci_nic_membar_lookup(
+	__in				efsys_pci_config_t *espcp,
+	__out				efx_bar_region_t *ebrp)
+{
+	boolean_t xilinx_tbl_found = B_FALSE;
+	unsigned int xilinx_tbl_bar;
+	efsys_dma_addr_t xilinx_tbl_offset;
+	size_t pci_capa_offset = 0;
+	boolean_t bar_found = B_FALSE;
+	efx_rc_t rc = ENOENT;
+
+	/*
+	 * SF-119689-TC Riverhead Host Interface section 4.2.2. describes
+	 * the following discovery steps.
+	 */
+	while (1) {
+		rc = efx_pci_find_next_xilinx_cap_table(espcp, &pci_capa_offset,
+							&xilinx_tbl_bar,
+							&xilinx_tbl_offset);
+		if (rc != 0) {
+			/*
+			 * SF-119689-TC Riverhead Host Interface section 4.2.2.
+			 * defines the following fallbacks for the memory bar
+			 * and the offset when no Xilinx capabilities table is
+			 * found.
+			 */
+			if (rc == ENOENT && xilinx_tbl_found == B_FALSE) {
+				ebrp->ebr_type = EFX_BAR_TYPE_MEM;
+				ebrp->ebr_index = EFX_MEM_BAR_RIVERHEAD;
+				ebrp->ebr_offset = 0;
+				ebrp->ebr_length = 0;
+				bar_found = B_TRUE;
+				break;
+			} else {
+				goto fail1;
+			}
+
+		}
+
+		xilinx_tbl_found = B_TRUE;
+	}
+
+	if (bar_found == B_FALSE)
+		goto fail2;
+
+	return (0);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+#endif /* EFSYS_OPT_RIVERHEAD && EFSYS_OPT_PCI */
diff --git a/drivers/common/sfc_efx/efsys.h b/drivers/common/sfc_efx/efsys.h
index e191cb5b6a..de1c1c38e3 100644
--- a/drivers/common/sfc_efx/efsys.h
+++ b/drivers/common/sfc_efx/efsys.h
@@ -162,6 +162,8 @@ prefetch_read_once(const volatile void *addr)
 
 #define EFSYS_OPT_MCDI_PROXY_AUTH_SERVER 0
 
+#define EFSYS_OPT_PCI 0
+
 /* ID */
 
 typedef struct __efsys_identifier_s efsys_identifier_t;
diff --git a/drivers/common/sfc_efx/rte_common_sfc_efx_version.map b/drivers/common/sfc_efx/rte_common_sfc_efx_version.map
index 16fffee321..627469a025 100644
--- a/drivers/common/sfc_efx/rte_common_sfc_efx_version.map
+++ b/drivers/common/sfc_efx/rte_common_sfc_efx_version.map
@@ -19,6 +19,7 @@ INTERNAL {
 	efx_evq_size;
 
 	efx_family;
+	efx_family_probe_bar;
 
 	efx_filter_fini;
 	efx_filter_init;
