common/sfc_efx/base: support NIC DMA memory regions API
authorAndrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
Wed, 17 Nov 2021 07:05:44 +0000 (10:05 +0300)
committerFerruh Yigit <ferruh.yigit@intel.com>
Wed, 17 Nov 2021 11:37:07 +0000 (12:37 +0100)
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
drivers/common/sfc_efx/base/efx.h
drivers/common/sfc_efx/base/efx_impl.h
drivers/common/sfc_efx/base/efx_mcdi.c
drivers/common/sfc_efx/base/efx_mcdi.h
drivers/common/sfc_efx/base/efx_nic.c
drivers/common/sfc_efx/base/siena_nic.c
drivers/common/sfc_efx/version.map

index 72d2caa..355d274 100644 (file)
@@ -1854,6 +1854,51 @@ fail1:
        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:
index f08a004..4d3210f 100644 (file)
@@ -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
index eda41b4..71c8351 100644 (file)
@@ -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;
index cdf7181..9189a7a 100644 (file)
@@ -3236,4 +3236,208 @@ fail1:
 
 #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 */
index 96f237b..c91ea41 100644 (file)
@@ -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)
index 9fe0933..172488e 100644 (file)
@@ -1300,3 +1300,463 @@ fail1:
 
        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);
+}
index 8b810d3..e425991 100644 (file)
@@ -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:
index 765ca39..3e57791 100644 (file)
@@ -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;