[v2,1/2] common/sfc_efx/base: support NIC DMA memory regions API

Message ID 20211117070545.4004374-2-andrew.rybchenko@oktetlabs.ru (mailing list archive)
State Accepted, archived
Delegated to: Ferruh Yigit
Headers
Series net/sfc: support SN1022 SoC |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Andrew Rybchenko Nov. 17, 2021, 7:05 a.m. UTC
  NIC DMA memory regions API allows to establish mapping of DMA addresses
used by NIC to host IOVA understood by the host when IOMMU is absent
and NIC cannot address entire host IOVA space because of too small
DMA mask.

The API does not allow to address entire host IOVA space, but allows
arbitrary regions of the space really used for the NIC DMA.

A DMA region needs to be mapped in order to perform MCDI initialization.
Since the NIC has not been probed at that point, its configuration cannot
be accessed and there an UNKNOWN mapping type is assumed.

Signed-off-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
Reviewed-by: Andy Moreton <amoreton@xilinx.com>
---
 drivers/common/sfc_efx/base/ef10_nic.c  |  51 +++
 drivers/common/sfc_efx/base/efx.h       |  46 +++
 drivers/common/sfc_efx/base/efx_impl.h  |  20 ++
 drivers/common/sfc_efx/base/efx_mcdi.c  | 204 +++++++++++
 drivers/common/sfc_efx/base/efx_mcdi.h  |  31 ++
 drivers/common/sfc_efx/base/efx_nic.c   | 460 ++++++++++++++++++++++++
 drivers/common/sfc_efx/base/siena_nic.c |   2 +
 drivers/common/sfc_efx/version.map      |   3 +
 8 files changed, 817 insertions(+)
  

Patch

diff --git a/drivers/common/sfc_efx/base/ef10_nic.c b/drivers/common/sfc_efx/base/ef10_nic.c
index 72d2caadb8..355d274470 100644
--- a/drivers/common/sfc_efx/base/ef10_nic.c
+++ b/drivers/common/sfc_efx/base/ef10_nic.c
@@ -1854,6 +1854,51 @@  ef10_external_port_mapping(
 	return (rc);
 }
 
+static __checkReturn	efx_rc_t
+efx_mcdi_get_nic_addr_caps(
+	__in		efx_nic_t *enp)
+{
+	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+	uint32_t mapping_type;
+	efx_rc_t rc;
+
+	rc = efx_mcdi_get_nic_addr_info(enp, &mapping_type);
+	if (rc != 0) {
+		if (rc == ENOTSUP) {
+			encp->enc_dma_mapping = EFX_NIC_DMA_MAPPING_FLAT;
+			goto out;
+		}
+		goto fail1;
+	}
+
+	switch (mapping_type) {
+	case MC_CMD_GET_DESC_ADDR_INFO_OUT_MAPPING_FLAT:
+		encp->enc_dma_mapping = EFX_NIC_DMA_MAPPING_FLAT;
+		break;
+	case MC_CMD_GET_DESC_ADDR_INFO_OUT_MAPPING_REGIONED:
+		encp->enc_dma_mapping = EFX_NIC_DMA_MAPPING_REGIONED;
+		rc = efx_mcdi_get_nic_addr_regions(enp,
+		    &enp->en_dma.end_u.endu_region_info);
+		if (rc != 0)
+			goto fail2;
+		break;
+	default:
+		goto fail3;
+	}
+
+out:
+	return (0);
+
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
 	__checkReturn	efx_rc_t
 efx_mcdi_nic_board_cfg(
 	__in		efx_nic_t *enp)
@@ -1994,8 +2039,14 @@  efx_mcdi_nic_board_cfg(
 	encp->enc_intr_vec_base = base;
 	encp->enc_intr_limit = nvec;
 
+	rc = efx_mcdi_get_nic_addr_caps(enp);
+	if (rc != 0)
+		goto fail12;
+
 	return (0);
 
+fail12:
+	EFSYS_PROBE(fail12);
 fail11:
 	EFSYS_PROBE(fail11);
 fail10:
diff --git a/drivers/common/sfc_efx/base/efx.h b/drivers/common/sfc_efx/base/efx.h
index f08a004536..4d3210f6b6 100644
--- a/drivers/common/sfc_efx/base/efx.h
+++ b/drivers/common/sfc_efx/base/efx.h
@@ -1444,6 +1444,14 @@  typedef enum efx_vi_window_shift_e {
 	EFX_VI_WINDOW_SHIFT_64K = 16,
 } efx_vi_window_shift_t;
 
+typedef enum efx_nic_dma_mapping_e {
+	EFX_NIC_DMA_MAPPING_UNKNOWN = 0,
+	EFX_NIC_DMA_MAPPING_FLAT,
+	EFX_NIC_DMA_MAPPING_REGIONED,
+
+	EFX_NIC_DMA_MAPPING_NTYPES
+} efx_nic_dma_mapping_t;
+
 typedef struct efx_nic_cfg_s {
 	uint32_t		enc_board_type;
 	uint32_t		enc_phy_type;
@@ -1633,6 +1641,8 @@  typedef struct efx_nic_cfg_s {
 	uint32_t		enc_filter_action_mark_max;
 	/* Port assigned to this PCI function */
 	uint32_t		enc_assigned_port;
+	/* NIC DMA mapping type */
+	efx_nic_dma_mapping_t	enc_dma_mapping;
 } efx_nic_cfg_t;
 
 #define	EFX_PCI_VF_INVALID 0xffff
@@ -4897,6 +4907,42 @@  efx_virtio_verify_features(
 
 #endif /* EFSYS_OPT_VIRTIO */
 
+LIBEFX_API
+extern	 __checkReturn	efx_rc_t
+efx_nic_dma_config_add(
+	__in		efx_nic_t *enp,
+	__in		efsys_dma_addr_t trgt_addr,
+	__in		size_t len,
+	__out_opt	efsys_dma_addr_t *nic_basep,
+	__out_opt	efsys_dma_addr_t *trgt_basep,
+	__out_opt	size_t *map_lenp);
+
+LIBEFX_API
+extern	 __checkReturn	efx_rc_t
+efx_nic_dma_reconfigure(
+	__in		efx_nic_t *enp);
+
+typedef enum efx_nic_dma_addr_type_e {
+	EFX_NIC_DMA_ADDR_MCDI_BUF,
+	EFX_NIC_DMA_ADDR_MAC_STATS_BUF,
+	EFX_NIC_DMA_ADDR_EVENT_RING,
+	EFX_NIC_DMA_ADDR_RX_RING,
+	EFX_NIC_DMA_ADDR_TX_RING,
+	EFX_NIC_DMA_ADDR_RX_BUF,
+	EFX_NIC_DMA_ADDR_TX_BUF,
+
+	EFX_NIC_DMA_ADDR_NTYPES
+} efx_nic_dma_addr_type_t;
+
+LIBEFX_API
+extern	__checkReturn	efx_rc_t
+efx_nic_dma_map(
+	__in		efx_nic_t *enp,
+	__in		efx_nic_dma_addr_type_t addr_type,
+	__in		efsys_dma_addr_t tgt_addr,
+	__in		size_t len,
+	__out		efsys_dma_addr_t *nic_addrp);
+
 #ifdef	__cplusplus
 }
 #endif
diff --git a/drivers/common/sfc_efx/base/efx_impl.h b/drivers/common/sfc_efx/base/efx_impl.h
index eda41b4be0..71c83515f7 100644
--- a/drivers/common/sfc_efx/base/efx_impl.h
+++ b/drivers/common/sfc_efx/base/efx_impl.h
@@ -428,6 +428,25 @@  typedef struct efx_nic_ops_s {
 #define	EFX_RXQ_LIMIT_TARGET 512
 #endif
 
+typedef struct efx_nic_dma_region_s {
+	efsys_dma_addr_t	endr_nic_base;
+	efsys_dma_addr_t	endr_trgt_base;
+	unsigned int		endr_window_log2;
+	unsigned int		endr_align_log2;
+	boolean_t		endr_inuse;
+} efx_nic_dma_region_t;
+
+typedef struct efx_nic_dma_region_info_s {
+	unsigned int		endri_count;
+	efx_nic_dma_region_t	*endri_regions;
+} efx_nic_dma_region_info_t;
+
+typedef struct efx_nic_dma_s {
+	union {
+		/* No configuration in the case flat mapping type */
+		efx_nic_dma_region_info_t	endu_region_info;
+	} end_u;
+} efx_nic_dma_t;
 
 #if EFSYS_OPT_FILTER
 
@@ -859,6 +878,7 @@  struct efx_nic_s {
 	const efx_rx_ops_t	*en_erxop;
 	efx_fw_variant_t	efv;
 	char			en_drv_version[EFX_DRV_VER_MAX];
+	efx_nic_dma_t		en_dma;
 #if EFSYS_OPT_FILTER
 	efx_filter_t		en_filter;
 	const efx_filter_ops_t	*en_efop;
diff --git a/drivers/common/sfc_efx/base/efx_mcdi.c b/drivers/common/sfc_efx/base/efx_mcdi.c
index cdf7181e0d..9189a7a8b3 100644
--- a/drivers/common/sfc_efx/base/efx_mcdi.c
+++ b/drivers/common/sfc_efx/base/efx_mcdi.c
@@ -3236,4 +3236,208 @@  efx_mcdi_fini_txq(
 
 #endif	/* EFSYS_OPT_RIVERHEAD || EFX_OPTS_EF10() */
 
+	__checkReturn	efx_rc_t
+efx_mcdi_get_nic_addr_info(
+	__in		efx_nic_t *enp,
+	__out		uint32_t *mapping_typep)
+{
+	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_DESC_ADDR_INFO_IN_LEN,
+		MC_CMD_GET_DESC_ADDR_INFO_OUT_LEN);
+	efx_mcdi_req_t req;
+	efx_rc_t rc;
+
+	req.emr_cmd = MC_CMD_GET_DESC_ADDR_INFO;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_GET_DESC_ADDR_INFO_IN_LEN;
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_GET_DESC_ADDR_INFO_OUT_LEN;
+
+	efx_mcdi_execute_quiet(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+	if (req.emr_out_length_used < MC_CMD_GET_DESC_ADDR_INFO_OUT_LEN) {
+		rc = EMSGSIZE;
+		goto fail2;
+	}
+
+	*mapping_typep =
+	    MCDI_OUT_DWORD(req, GET_DESC_ADDR_INFO_OUT_MAPPING_TYPE);
+
+	return (0);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn	efx_rc_t
+efx_mcdi_get_nic_addr_regions(
+	__in		efx_nic_t *enp,
+	__out		efx_nic_dma_region_info_t *endrip)
+{
+	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_DESC_ADDR_REGIONS_IN_LEN,
+		MC_CMD_GET_DESC_ADDR_REGIONS_OUT_LENMAX_MCDI2);
+	efx_xword_t *regions;
+	efx_mcdi_req_t req;
+	efx_rc_t rc;
+	size_t alloc_size;
+	unsigned int nregions;
+	unsigned int i;
+
+	req.emr_cmd = MC_CMD_GET_DESC_ADDR_REGIONS;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_GET_DESC_ADDR_REGIONS_IN_LEN;
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_GET_DESC_ADDR_REGIONS_OUT_LENMAX_MCDI2;
+
+	efx_mcdi_execute_quiet(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+	if (req.emr_out_length_used <
+	    MC_CMD_GET_DESC_ADDR_REGIONS_OUT_LENMIN) {
+		rc = EMSGSIZE;
+		goto fail2;
+	}
+
+	nregions = MC_CMD_GET_DESC_ADDR_REGIONS_OUT_REGIONS_NUM(
+	    req.emr_out_length_used);
+
+	EFX_STATIC_ASSERT(sizeof (*regions) == DESC_ADDR_REGION_LEN);
+	regions = MCDI_OUT2(req, efx_xword_t,
+	    GET_DESC_ADDR_REGIONS_OUT_REGIONS);
+
+	alloc_size = nregions * sizeof(endrip->endri_regions[0]);
+	if (alloc_size / sizeof (endrip->endri_regions[0]) != nregions) {
+		rc = ENOMEM;
+		goto fail3;
+	}
+
+	EFSYS_KMEM_ALLOC(enp->en_esip,
+	    alloc_size,
+	    endrip->endri_regions);
+	if (endrip->endri_regions == NULL) {
+		rc = ENOMEM;
+		goto fail4;
+	}
+
+	endrip->endri_count = nregions;
+	for (i = 0; i < nregions; ++i) {
+		efx_nic_dma_region_t *region_info;
+
+		region_info = &endrip->endri_regions[i];
+
+		region_info->endr_inuse = B_FALSE;
+
+		region_info->endr_nic_base =
+		    MCDI_OUT_INDEXED_MEMBER_QWORD(req,
+		        GET_DESC_ADDR_REGIONS_OUT_REGIONS, i,
+		        DESC_ADDR_REGION_DESC_ADDR_BASE);
+
+		region_info->endr_trgt_base =
+		    MCDI_OUT_INDEXED_MEMBER_QWORD(req,
+		        GET_DESC_ADDR_REGIONS_OUT_REGIONS, i,
+		        DESC_ADDR_REGION_TRGT_ADDR_BASE);
+
+		region_info->endr_window_log2 =
+		    MCDI_OUT_INDEXED_MEMBER_DWORD(req,
+		        GET_DESC_ADDR_REGIONS_OUT_REGIONS, i,
+		        DESC_ADDR_REGION_WINDOW_SIZE_LOG2);
+
+		region_info->endr_align_log2 =
+		    MCDI_OUT_INDEXED_MEMBER_DWORD(req,
+		        GET_DESC_ADDR_REGIONS_OUT_REGIONS, i,
+		        DESC_ADDR_REGION_TRGT_ADDR_ALIGN_LOG2);
+	}
+
+	return (0);
+
+fail4:
+	EFSYS_PROBE(fail4);
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn	efx_rc_t
+efx_mcdi_set_nic_addr_regions(
+	__in		efx_nic_t *enp,
+	__in		const efx_nic_dma_region_info_t *endrip)
+{
+	EFX_MCDI_DECLARE_BUF(payload,
+		MC_CMD_SET_DESC_ADDR_REGIONS_IN_LENMAX_MCDI2,
+		MC_CMD_SET_DESC_ADDR_REGIONS_OUT_LEN);
+	efx_qword_t *trgt_addr_base;
+	efx_mcdi_req_t req;
+	unsigned int i;
+	efx_rc_t rc;
+
+	if (endrip->endri_count >
+	    MC_CMD_SET_DESC_ADDR_REGIONS_IN_TRGT_ADDR_BASE_MAXNUM) {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	req.emr_cmd = MC_CMD_SET_DESC_ADDR_REGIONS;
+	req.emr_in_buf = payload;
+	req.emr_in_length =
+	    MC_CMD_SET_DESC_ADDR_REGIONS_IN_LEN(endrip->endri_count);
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_SET_DESC_ADDR_REGIONS_OUT_LEN;
+
+	EFX_STATIC_ASSERT(sizeof (*trgt_addr_base) ==
+	    MC_CMD_SET_DESC_ADDR_REGIONS_IN_TRGT_ADDR_BASE_LEN);
+	trgt_addr_base = MCDI_OUT2(req, efx_qword_t,
+	    SET_DESC_ADDR_REGIONS_IN_TRGT_ADDR_BASE);
+
+	for (i = 0; i < endrip->endri_count; ++i) {
+		const efx_nic_dma_region_t *region_info;
+
+		region_info = &endrip->endri_regions[i];
+
+		if (region_info->endr_inuse != B_TRUE)
+			continue;
+
+		EFX_STATIC_ASSERT(sizeof (1U) * 8 >=
+		    MC_CMD_SET_DESC_ADDR_REGIONS_IN_TRGT_ADDR_BASE_MAXNUM);
+		MCDI_IN_SET_DWORD(req,
+		    SET_DESC_ADDR_REGIONS_IN_SET_REGION_MASK, 1U << i);
+
+		MCDI_IN_SET_INDEXED_QWORD(req,
+		    SET_DESC_ADDR_REGIONS_IN_TRGT_ADDR_BASE, i,
+		    region_info->endr_trgt_base);
+	}
+
+	efx_mcdi_execute_quiet(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail2;
+	}
+
+	return (0);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
 #endif	/* EFSYS_OPT_MCDI */
diff --git a/drivers/common/sfc_efx/base/efx_mcdi.h b/drivers/common/sfc_efx/base/efx_mcdi.h
index 96f237b1b0..c91ea41911 100644
--- a/drivers/common/sfc_efx/base/efx_mcdi.h
+++ b/drivers/common/sfc_efx/base/efx_mcdi.h
@@ -289,6 +289,26 @@  efx_mcdi_phy_module_get_info(
 	__in			size_t len,
 	__out_bcount(len)	uint8_t *data);
 
+LIBEFX_INTERNAL
+extern	__checkReturn	efx_rc_t
+efx_mcdi_get_nic_addr_info(
+	__in		efx_nic_t *enp,
+	__out		uint32_t *mapping_typep);
+
+struct efx_nic_dma_region_info_s;
+
+LIBEFX_INTERNAL
+extern	__checkReturn	efx_rc_t
+efx_mcdi_get_nic_addr_regions(
+	__in		efx_nic_t *enp,
+	__out		struct efx_nic_dma_region_info_s *endrip);
+
+LIBEFX_INTERNAL
+extern	__checkReturn	efx_rc_t
+efx_mcdi_set_nic_addr_regions(
+	__in		efx_nic_t *enp,
+	__in		const struct efx_nic_dma_region_info_s *endrip);
+
 #define	MCDI_IN(_emr, _type, _ofst)					\
 	((_type *)((_emr).emr_in_buf + (_ofst)))
 
@@ -315,6 +335,17 @@  efx_mcdi_phy_module_get_info(
 	EFX_POPULATE_DWORD_1(*(MCDI_IN2(_emr, efx_dword_t, _ofst) +	\
 			     (_idx)), EFX_DWORD_0, _value)		\
 
+#define	MCDI_IN_SET_QWORD(_emr, _ofst, _value)				\
+	EFX_POPULATE_QWORD_2(*MCDI_IN2(_emr, efx_qword_t, _ofst),	\
+		EFX_DWORD_0, ((_value) & 0xffffffff),			\
+		EFX_DWORD_1, ((_value) >> 32))
+
+#define	MCDI_IN_SET_INDEXED_QWORD(_emr, _ofst, _idx, _value)		\
+	EFX_POPULATE_QWORD_2(*(MCDI_IN2(_emr, efx_qword_t, _ofst) +	\
+			(_idx)),					\
+		EFX_DWORD_0, ((_value) & 0xffffffff),	\
+		EFX_DWORD_1, ((_value) >> 32))
+
 #define	MCDI_IN_POPULATE_DWORD_1(_emr, _ofst, _field1, _value1)		\
 	EFX_POPULATE_DWORD_1(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
 		MC_CMD_ ## _field1, _value1)
diff --git a/drivers/common/sfc_efx/base/efx_nic.c b/drivers/common/sfc_efx/base/efx_nic.c
index 9fe0933772..172488e083 100644
--- a/drivers/common/sfc_efx/base/efx_nic.c
+++ b/drivers/common/sfc_efx/base/efx_nic.c
@@ -1300,3 +1300,463 @@  efx_nic_check_pcie_link_speed(
 
 	return (rc);
 }
+
+/* Required en_eslp lock held */
+static __checkReturn	efx_rc_t
+efx_nic_dma_config_regioned_find_region(
+	__in		const efx_nic_t *enp,
+	__in		efsys_dma_addr_t trgt_addr,
+	__in		size_t len,
+	__out		const efx_nic_dma_region_t **regionp)
+{
+	const efx_nic_dma_region_info_t *region_info;
+	const efx_nic_dma_region_t *region;
+	unsigned int i;
+	efx_rc_t rc;
+
+	if (efx_nic_cfg_get(enp)->enc_dma_mapping !=
+	    EFX_NIC_DMA_MAPPING_REGIONED) {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	region_info = &enp->en_dma.end_u.endu_region_info;
+
+	for (i = 0; i < region_info->endri_count; ++i) {
+		efsys_dma_addr_t offset;
+
+		region = &region_info->endri_regions[i];
+		if (region->endr_inuse == B_FALSE)
+			continue;
+
+		if (trgt_addr < region->endr_trgt_base)
+			continue;
+
+		EFSYS_ASSERT3U(region->endr_window_log2, <, 64);
+		offset = trgt_addr - region->endr_trgt_base;
+		if (offset + len > (1ULL << region->endr_window_log2))
+			continue;
+
+		*regionp = region;
+		return (0);
+	}
+
+	rc = ENOENT;
+	goto fail2;
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+static __checkReturn	efx_rc_t
+efx_nic_dma_config_regioned_add_region(
+	__in		efx_nic_t *enp,
+	__in		efsys_dma_addr_t trgt_addr,
+	__in		size_t len,
+	__out		const efx_nic_dma_region_t **regionp)
+{
+	efx_nic_dma_region_info_t *region_info;
+	efx_nic_dma_region_t *region;
+	unsigned int i;
+	efx_rc_t rc;
+
+	if (efx_nic_cfg_get(enp)->enc_dma_mapping !=
+	    EFX_NIC_DMA_MAPPING_REGIONED) {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	region_info = &enp->en_dma.end_u.endu_region_info;
+
+	for (i = 0; i < region_info->endri_count; ++i) {
+		efsys_dma_addr_t trgt_base;
+		efsys_dma_addr_t offset;
+
+		region = &region_info->endri_regions[i];
+		if (region->endr_inuse == B_TRUE)
+			continue;
+
+		/*
+		 * Align target address base in accordance with
+		 * the region requirements.
+		 */
+		EFSYS_ASSERT3U(region->endr_align_log2, <, 64);
+		trgt_base = EFX_P2ALIGN(efsys_dma_addr_t, trgt_addr,
+		    (1ULL << region->endr_align_log2));
+
+		offset = trgt_addr - trgt_base;
+
+		/* Check if region window is sufficient */
+		EFSYS_ASSERT3U(region->endr_window_log2, <, 64);
+		if (offset + len > (1ULL << region->endr_window_log2))
+			continue;
+
+		region->endr_trgt_base = trgt_base;
+		region->endr_inuse = B_TRUE;
+
+		*regionp = region;
+		return (0);
+	}
+
+	/* No suitable free region found */
+	rc = ENOMEM;
+	goto fail2;
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+static	__checkReturn	efx_rc_t
+efx_nic_dma_config_regioned_add(
+	__in		efx_nic_t *enp,
+	__in		efsys_dma_addr_t trgt_addr,
+	__in		size_t len,
+	__out_opt	efsys_dma_addr_t *nic_basep,
+	__out_opt	efsys_dma_addr_t *trgt_basep,
+	__out_opt	size_t *map_lenp)
+{
+	const efx_nic_dma_region_t *region;
+	efsys_lock_state_t state;
+	efx_rc_t rc;
+
+	EFSYS_LOCK(enp->en_eslp, state);
+
+	rc = efx_nic_dma_config_regioned_find_region(enp, trgt_addr, len,
+	    &region);
+	switch (rc) {
+	case 0:
+		/* Already covered by existing mapping */
+		break;
+	case ENOENT:
+		/* No existing mapping found */
+		rc = efx_nic_dma_config_regioned_add_region(enp,
+		    trgt_addr, len, &region);
+		if (rc != 0)
+			goto fail1;
+		break;
+	default:
+		goto fail2;
+	}
+
+	if (nic_basep != NULL)
+		*nic_basep = region->endr_nic_base;
+	if (trgt_basep != NULL)
+		*trgt_basep = region->endr_trgt_base;
+	if (map_lenp != NULL)
+		*map_lenp = 1ULL << region->endr_window_log2;
+
+	EFSYS_UNLOCK(enp->en_eslp, state);
+
+	return (0);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	EFSYS_UNLOCK(enp->en_eslp, state);
+
+	return (rc);
+}
+
+	 __checkReturn	efx_rc_t
+efx_nic_dma_config_add(
+	__in		efx_nic_t *enp,
+	__in		efsys_dma_addr_t trgt_addr,
+	__in		size_t len,
+	__out_opt	efsys_dma_addr_t *nic_basep,
+	__out_opt	efsys_dma_addr_t *trgt_basep,
+	__out_opt	size_t *map_lenp)
+{
+	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
+	efx_rc_t rc;
+
+	switch (encp->enc_dma_mapping) {
+	case EFX_NIC_DMA_MAPPING_FLAT:
+		/* No mapping is required */
+		if (nic_basep != NULL)
+			*nic_basep = 0;
+		if (trgt_basep != NULL)
+			*trgt_basep = 0;
+		if (map_lenp != NULL)
+			*map_lenp = 0;
+		break;
+	case EFX_NIC_DMA_MAPPING_REGIONED:
+		rc = efx_nic_dma_config_regioned_add(enp, trgt_addr, len,
+		    nic_basep, trgt_basep, map_lenp);
+		if (rc != 0)
+			goto fail1;
+		break;
+	case EFX_NIC_DMA_MAPPING_UNKNOWN:
+	default:
+		rc = ENOTSUP;
+		goto fail2;
+	}
+
+	return (0);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+static	 __checkReturn	efx_rc_t
+efx_nic_dma_reconfigure_regioned(
+	__in		efx_nic_t *enp)
+{
+	efx_rc_t rc;
+
+	rc = efx_mcdi_set_nic_addr_regions(enp,
+	    &enp->en_dma.end_u.endu_region_info);
+	if (rc != 0)
+		goto fail1;
+
+	return (0);
+
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+
+}
+
+	 __checkReturn	efx_rc_t
+efx_nic_dma_reconfigure(
+	__in		efx_nic_t *enp)
+{
+	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
+	efx_rc_t rc;
+
+	switch (encp->enc_dma_mapping) {
+	case EFX_NIC_DMA_MAPPING_UNKNOWN:
+	case EFX_NIC_DMA_MAPPING_FLAT:
+		/* Nothing to do */
+		break;
+	case EFX_NIC_DMA_MAPPING_REGIONED:
+		rc = efx_nic_dma_reconfigure_regioned(enp);
+		if (rc != 0)
+			goto fail1;
+		break;
+	default:
+		rc = ENOTSUP;
+		goto fail2;
+	}
+
+	return (0);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+static __checkReturn	efx_rc_t
+efx_nic_dma_unknown_map(
+	__in		efx_nic_t *enp,
+	__in		efx_nic_dma_addr_type_t addr_type,
+	__in		efsys_dma_addr_t trgt_addr,
+	__in		size_t len,
+	__out		efsys_dma_addr_t *nic_addrp)
+{
+	efx_rc_t rc;
+
+	/* This function may be called before the NIC has been probed. */
+	if (enp->en_mod_flags & EFX_MOD_PROBE) {
+		EFSYS_ASSERT3U(efx_nic_cfg_get(enp)->enc_dma_mapping, ==,
+		    EFX_NIC_DMA_MAPPING_UNKNOWN);
+	}
+
+	switch (addr_type) {
+	case EFX_NIC_DMA_ADDR_MCDI_BUF:
+		/*
+		 * MC cares about MCDI buffer mapping itself since it cannot
+		 * be really mapped using MCDI because mapped MCDI
+		 * buffer is required to execute MCDI commands.
+		 */
+		*nic_addrp = trgt_addr;
+		break;
+
+	case EFX_NIC_DMA_ADDR_MAC_STATS_BUF:
+	case EFX_NIC_DMA_ADDR_EVENT_RING:
+	case EFX_NIC_DMA_ADDR_RX_RING:
+	case EFX_NIC_DMA_ADDR_TX_RING:
+	case EFX_NIC_DMA_ADDR_RX_BUF:
+	case EFX_NIC_DMA_ADDR_TX_BUF:
+		/* Mapping type must be discovered first */
+		rc = EFAULT;
+		goto fail1;
+
+	default:
+		rc = EINVAL;
+		goto fail2;
+	}
+
+	return (0);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+static __checkReturn	efx_rc_t
+efx_nic_dma_flat_map(
+	__in		efx_nic_t *enp,
+	__in		efx_nic_dma_addr_type_t addr_type,
+	__in		efsys_dma_addr_t trgt_addr,
+	__in		size_t len,
+	__out		efsys_dma_addr_t *nic_addrp)
+{
+	_NOTE(ARGUNUSED(addr_type, len))
+
+	EFSYS_ASSERT3U(efx_nic_cfg_get(enp)->enc_dma_mapping, ==,
+	    EFX_NIC_DMA_MAPPING_FLAT);
+
+	/* No re-mapping is required */
+	*nic_addrp = trgt_addr;
+
+	return (0);
+}
+
+static __checkReturn	efx_rc_t
+efx_nic_dma_regioned_map(
+	__in		efx_nic_t *enp,
+	__in		efx_nic_dma_addr_type_t addr_type,
+	__in		efsys_dma_addr_t trgt_addr,
+	__in		size_t len,
+	__out		efsys_dma_addr_t *nic_addrp)
+{
+	const efx_nic_dma_region_t *region;
+	efsys_lock_state_t state;
+	efx_rc_t rc;
+
+	if (efx_nic_cfg_get(enp)->enc_dma_mapping !=
+	    EFX_NIC_DMA_MAPPING_REGIONED) {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	switch (addr_type) {
+	case EFX_NIC_DMA_ADDR_MCDI_BUF:
+	case EFX_NIC_DMA_ADDR_MAC_STATS_BUF:
+		/*
+		 * MC cares about MCDI buffer mapping itself since it cannot
+		 * be really mapped using MCDI because mapped MCDI buffer is
+		 * required to execute MCDI commands. It is not a problem
+		 * for MAC stats buffer, but since MC can care about mapping
+		 * itself, it may be done for MAC stats buffer as well.
+		 */
+		*nic_addrp = trgt_addr;
+		goto out;
+
+	case EFX_NIC_DMA_ADDR_EVENT_RING:
+	case EFX_NIC_DMA_ADDR_RX_RING:
+	case EFX_NIC_DMA_ADDR_TX_RING:
+	case EFX_NIC_DMA_ADDR_RX_BUF:
+	case EFX_NIC_DMA_ADDR_TX_BUF:
+		/* Rings and buffer addresses should be mapped */
+		break;
+
+	default:
+		rc = EINVAL;
+		goto fail2;
+	}
+
+	EFSYS_LOCK(enp->en_eslp, state);
+
+	rc = efx_nic_dma_config_regioned_find_region(enp, trgt_addr, len,
+	    &region);
+	if (rc != 0)
+		goto fail3;
+
+	*nic_addrp = region->endr_nic_base +
+		(trgt_addr - region->endr_trgt_base);
+
+	EFSYS_UNLOCK(enp->en_eslp, state);
+
+out:
+	return (0);
+
+fail3:
+	EFSYS_PROBE(fail3);
+	EFSYS_UNLOCK(enp->en_eslp, state);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn	efx_rc_t
+efx_nic_dma_map(
+	__in		efx_nic_t *enp,
+	__in		efx_nic_dma_addr_type_t addr_type,
+	__in		efsys_dma_addr_t trgt_addr,
+	__in		size_t len,
+	__out		efsys_dma_addr_t *nic_addrp)
+{
+	efx_nic_dma_mapping_t mapping;
+	efx_rc_t rc;
+
+	/*
+	 * We cannot check configuration of a NIC that hasn't been probed.
+	 * Use EFX_NIC_DMA_MAPPING_UNKNOWN by default.
+	 */
+	if ((enp->en_mod_flags & EFX_MOD_PROBE) == 0)
+		mapping = EFX_NIC_DMA_MAPPING_UNKNOWN;
+	else
+		mapping = efx_nic_cfg_get(enp)->enc_dma_mapping;
+
+	switch (mapping) {
+	case EFX_NIC_DMA_MAPPING_UNKNOWN:
+		rc = efx_nic_dma_unknown_map(enp, addr_type, trgt_addr,
+		    len, nic_addrp);
+		if (rc != 0)
+			goto fail1;
+		break;
+	case EFX_NIC_DMA_MAPPING_FLAT:
+		rc = efx_nic_dma_flat_map(enp, addr_type, trgt_addr,
+		    len, nic_addrp);
+		if (rc != 0)
+			goto fail2;
+		break;
+	case EFX_NIC_DMA_MAPPING_REGIONED:
+		rc = efx_nic_dma_regioned_map(enp, addr_type, trgt_addr,
+		    len, nic_addrp);
+		if (rc != 0)
+			goto fail3;
+		break;
+	default:
+		rc = ENOTSUP;
+		goto fail4;
+	}
+
+	return (0);
+
+fail4:
+	EFSYS_PROBE(fail4);
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
diff --git a/drivers/common/sfc_efx/base/siena_nic.c b/drivers/common/sfc_efx/base/siena_nic.c
index 8b810d3ae3..e42599131a 100644
--- a/drivers/common/sfc_efx/base/siena_nic.c
+++ b/drivers/common/sfc_efx/base/siena_nic.c
@@ -199,6 +199,8 @@  siena_board_cfg(
 	encp->enc_mae_supported = B_FALSE;
 	encp->enc_mae_admin = B_FALSE;
 
+	encp->enc_dma_mapping = EFX_NIC_DMA_MAPPING_FLAT;
+
 	return (0);
 
 fail2:
diff --git a/drivers/common/sfc_efx/version.map b/drivers/common/sfc_efx/version.map
index 765ca39332..3e57791c99 100644
--- a/drivers/common/sfc_efx/version.map
+++ b/drivers/common/sfc_efx/version.map
@@ -160,6 +160,9 @@  INTERNAL {
 	efx_nic_check_pcie_link_speed;
 	efx_nic_create;
 	efx_nic_destroy;
+	efx_nic_dma_config_add;
+	efx_nic_dma_map;
+	efx_nic_dma_reconfigure;
 	efx_nic_fini;
 	efx_nic_get_bar_region;
 	efx_nic_get_board_info;