common/sfc_efx/base: support NIC DMA memory regions API
[dpdk.git] / drivers / common / sfc_efx / base / efx_nic.c
index 9d6961e..172488e 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: BSD-3-Clause
  *
- * Copyright(c) 2019-2020 Xilinx, Inc.
+ * Copyright(c) 2019-2021 Xilinx, Inc.
  * Copyright(c) 2007-2019 Solarflare Communications Inc.
  */
 
@@ -103,6 +103,48 @@ 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,
+       __in            const efx_pci_ops_t *epop,
+       __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, epop, 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
 
@@ -188,12 +230,34 @@ static const efx_nic_ops_t        __efx_nic_medford2_ops = {
 
 #endif /* EFSYS_OPT_MEDFORD2 */
 
+#if EFSYS_OPT_RIVERHEAD
+
+static const efx_nic_ops_t     __efx_nic_riverhead_ops = {
+       rhead_nic_probe,                /* eno_probe */
+       rhead_board_cfg,                /* eno_board_cfg */
+       rhead_nic_set_drv_limits,       /* eno_set_drv_limits */
+       rhead_nic_reset,                /* eno_reset */
+       rhead_nic_init,                 /* eno_init */
+       rhead_nic_get_vi_pool,          /* eno_get_vi_pool */
+       rhead_nic_get_bar_region,       /* eno_get_bar_region */
+       rhead_nic_hw_unavailable,       /* eno_hw_unavailable */
+       rhead_nic_set_hw_unavailable,   /* eno_set_hw_unavailable */
+#if EFSYS_OPT_DIAG
+       rhead_nic_register_test,        /* eno_register_test */
+#endif /* EFSYS_OPT_DIAG */
+       rhead_nic_fini,                 /* eno_fini */
+       rhead_nic_unprobe,              /* eno_unprobe */
+};
+
+#endif /* EFSYS_OPT_RIVERHEAD */
+
 
        __checkReturn   efx_rc_t
 efx_nic_create(
        __in            efx_family_t family,
        __in            efsys_identifier_t *esip,
        __in            efsys_bar_t *esbp,
+       __in            uint32_t fcw_offset,
        __in            efsys_lock_t *eslp,
        __deref_out     efx_nic_t **enpp)
 {
@@ -285,11 +349,30 @@ efx_nic_create(
                break;
 #endif /* EFSYS_OPT_MEDFORD2 */
 
+#if EFSYS_OPT_RIVERHEAD
+       case EFX_FAMILY_RIVERHEAD:
+               enp->en_enop = &__efx_nic_riverhead_ops;
+               enp->en_features =
+                   EFX_FEATURE_IPV6 |
+                   EFX_FEATURE_LINK_EVENTS |
+                   EFX_FEATURE_PERIODIC_MAC_STATS |
+                   EFX_FEATURE_MCDI |
+                   EFX_FEATURE_MAC_HEADER_FILTERS |
+                   EFX_FEATURE_MCDI_DMA;
+               enp->en_arch.ef10.ena_fcw_base = fcw_offset;
+               break;
+#endif /* EFSYS_OPT_RIVERHEAD */
+
        default:
                rc = ENOTSUP;
                goto fail2;
        }
 
+       if ((family != EFX_FAMILY_RIVERHEAD) && (fcw_offset != 0)) {
+               rc = EINVAL;
+               goto fail3;
+       }
+
        enp->en_family = family;
        enp->en_esip = esip;
        enp->en_esbp = esbp;
@@ -299,6 +382,8 @@ efx_nic_create(
 
        return (0);
 
+fail3:
+       EFSYS_PROBE(fail3);
 fail2:
        EFSYS_PROBE(fail2);
 
@@ -706,6 +791,52 @@ efx_nic_get_fw_version(
 
        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_nic_get_board_info(
+       __in            efx_nic_t *enp,
+       __out           efx_nic_board_info_t *board_infop)
+{
+       efx_mcdi_version_t ver;
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
+       EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
+
+       rc = efx_mcdi_get_version(enp, EFX_MCDI_VERSION_BOARD_INFO, &ver);
+       if (rc == EMSGSIZE) {
+               /*
+                * Typically, EMSGSIZE is returned by above call in the
+                * case when the NIC does not provide extra information.
+                */
+               rc = ENOTSUP;
+               goto fail1;
+       } else if (rc != 0) {
+               goto fail2;
+       }
+
+       if ((ver.emv_flags & EFX_MCDI_VERSION_BOARD_INFO) == 0) {
+               rc = ENOTSUP;
+               goto fail3;
+       }
+
+       memcpy(board_infop, &ver.emv_board_info, sizeof (*board_infop));
+
+       /* MCDI should provide NUL-terminated strings, but stay vigilant. */
+       board_infop->enbi_serial[sizeof (board_infop->enbi_serial) - 1] = '\0';
+       board_infop->enbi_name[sizeof (board_infop->enbi_name) - 1] = '\0';
+
+       return (0);
+
 fail3:
        EFSYS_PROBE(fail3);
 fail2:
@@ -1169,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);
+}