+static uint32_t bnxt_map_reset_regs(struct bnxt *bp, uint32_t reg)
+{
+ uint32_t offset;
+
+ /* Only pre-map the reset GRC registers using window 3 */
+ rte_write32(reg & 0xfffff000, (uint8_t *)bp->bar0 +
+ BNXT_GRCPF_REG_WINDOW_BASE_OUT + 8);
+
+ offset = BNXT_GRCP_WINDOW_3_BASE + (reg & 0xffc);
+
+ return offset;
+}
+
+int bnxt_map_fw_health_status_regs(struct bnxt *bp)
+{
+ struct bnxt_error_recovery_info *info = bp->recovery_info;
+ uint32_t reg_base = 0xffffffff;
+ int i;
+
+ /* Only pre-map the monitoring GRC registers using window 2 */
+ for (i = 0; i < BNXT_FW_STATUS_REG_CNT; i++) {
+ uint32_t reg = info->status_regs[i];
+
+ if (BNXT_FW_STATUS_REG_TYPE(reg) != BNXT_FW_STATUS_REG_TYPE_GRC)
+ continue;
+
+ if (reg_base == 0xffffffff)
+ reg_base = reg & 0xfffff000;
+ if ((reg & 0xfffff000) != reg_base)
+ return -ERANGE;
+
+ /* Use mask 0xffc as the Lower 2 bits indicates
+ * address space location
+ */
+ info->mapped_status_regs[i] = BNXT_GRCP_WINDOW_2_BASE +
+ (reg & 0xffc);
+ }
+
+ if (reg_base == 0xffffffff)
+ return 0;
+
+ rte_write32(reg_base, (uint8_t *)bp->bar0 +
+ BNXT_GRCPF_REG_WINDOW_BASE_OUT + 4);
+
+ return 0;
+}
+
+static void bnxt_write_fw_reset_reg(struct bnxt *bp, uint32_t index)
+{
+ struct bnxt_error_recovery_info *info = bp->recovery_info;
+ uint32_t delay = info->delay_after_reset[index];
+ uint32_t val = info->reset_reg_val[index];
+ uint32_t reg = info->reset_reg[index];
+ uint32_t type, offset;
+
+ type = BNXT_FW_STATUS_REG_TYPE(reg);
+ offset = BNXT_FW_STATUS_REG_OFF(reg);
+
+ switch (type) {
+ case BNXT_FW_STATUS_REG_TYPE_CFG:
+ rte_pci_write_config(bp->pdev, &val, sizeof(val), offset);
+ break;
+ case BNXT_FW_STATUS_REG_TYPE_GRC:
+ offset = bnxt_map_reset_regs(bp, offset);
+ rte_write32(val, (uint8_t *)bp->bar0 + offset);
+ break;
+ case BNXT_FW_STATUS_REG_TYPE_BAR0:
+ rte_write32(val, (uint8_t *)bp->bar0 + offset);
+ break;
+ }
+ /* wait on a specific interval of time until core reset is complete */
+ if (delay)
+ rte_delay_ms(delay);
+}
+
+static void bnxt_dev_cleanup(struct bnxt *bp)
+{
+ bp->eth_dev->data->dev_link.link_status = 0;
+ bp->link_info->link_up = 0;
+ if (bp->eth_dev->data->dev_started)
+ bnxt_dev_stop_op(bp->eth_dev);
+
+ bnxt_uninit_resources(bp, true);
+}
+
+static int bnxt_restore_vlan_filters(struct bnxt *bp)
+{
+ struct rte_eth_dev *dev = bp->eth_dev;
+ struct rte_vlan_filter_conf *vfc;
+ int vidx, vbit, rc;
+ uint16_t vlan_id;
+
+ for (vlan_id = 1; vlan_id <= RTE_ETHER_MAX_VLAN_ID; vlan_id++) {
+ vfc = &dev->data->vlan_filter_conf;
+ vidx = vlan_id / 64;
+ vbit = vlan_id % 64;
+
+ /* Each bit corresponds to a VLAN id */
+ if (vfc->ids[vidx] & (UINT64_C(1) << vbit)) {
+ rc = bnxt_add_vlan_filter(bp, vlan_id);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int bnxt_restore_mac_filters(struct bnxt *bp)
+{
+ struct rte_eth_dev *dev = bp->eth_dev;
+ struct rte_eth_dev_info dev_info;
+ struct rte_ether_addr *addr;
+ uint64_t pool_mask;
+ uint32_t pool = 0;
+ uint16_t i;
+ int rc;
+
+ if (BNXT_VF(bp) && !BNXT_VF_IS_TRUSTED(bp))
+ return 0;
+
+ rc = bnxt_dev_info_get_op(dev, &dev_info);
+ if (rc)
+ return rc;
+
+ /* replay MAC address configuration */
+ for (i = 1; i < dev_info.max_mac_addrs; i++) {
+ addr = &dev->data->mac_addrs[i];
+
+ /* skip zero address */
+ if (rte_is_zero_ether_addr(addr))
+ continue;
+
+ pool = 0;
+ pool_mask = dev->data->mac_pool_sel[i];
+
+ do {
+ if (pool_mask & 1ULL) {
+ rc = bnxt_mac_addr_add_op(dev, addr, i, pool);
+ if (rc)
+ return rc;
+ }
+ pool_mask >>= 1;
+ pool++;
+ } while (pool_mask);
+ }
+
+ return 0;
+}
+
+static int bnxt_restore_filters(struct bnxt *bp)
+{
+ struct rte_eth_dev *dev = bp->eth_dev;
+ int ret = 0;
+
+ if (dev->data->all_multicast) {
+ ret = bnxt_allmulticast_enable_op(dev);
+ if (ret)
+ return ret;
+ }
+ if (dev->data->promiscuous) {
+ ret = bnxt_promiscuous_enable_op(dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = bnxt_restore_mac_filters(bp);
+ if (ret)
+ return ret;
+
+ ret = bnxt_restore_vlan_filters(bp);
+ /* TODO restore other filters as well */
+ return ret;
+}
+
+static void bnxt_dev_recover(void *arg)
+{
+ struct bnxt *bp = arg;
+ int timeout = bp->fw_reset_max_msecs;
+ int rc = 0;
+
+ /* Clear Error flag so that device re-init should happen */
+ bp->flags &= ~BNXT_FLAG_FATAL_ERROR;
+
+ do {
+ rc = bnxt_hwrm_ver_get(bp, SHORT_HWRM_CMD_TIMEOUT);
+ if (rc == 0)
+ break;
+ rte_delay_ms(BNXT_FW_READY_WAIT_INTERVAL);
+ timeout -= BNXT_FW_READY_WAIT_INTERVAL;
+ } while (rc && timeout);
+
+ if (rc) {
+ PMD_DRV_LOG(ERR, "FW is not Ready after reset\n");
+ goto err;
+ }
+
+ rc = bnxt_init_resources(bp, true);
+ if (rc) {
+ PMD_DRV_LOG(ERR,
+ "Failed to initialize resources after reset\n");
+ goto err;
+ }
+ /* clear reset flag as the device is initialized now */
+ bp->flags &= ~BNXT_FLAG_FW_RESET;
+
+ rc = bnxt_dev_start_op(bp->eth_dev);
+ if (rc) {
+ PMD_DRV_LOG(ERR, "Failed to start port after reset\n");
+ goto err_start;
+ }
+
+ rc = bnxt_restore_filters(bp);
+ if (rc)
+ goto err_start;
+
+ PMD_DRV_LOG(INFO, "Recovered from FW reset\n");
+ return;
+err_start:
+ bnxt_dev_stop_op(bp->eth_dev);
+err:
+ bp->flags |= BNXT_FLAG_FATAL_ERROR;
+ bnxt_uninit_resources(bp, false);
+ PMD_DRV_LOG(ERR, "Failed to recover from FW reset\n");
+}
+
+void bnxt_dev_reset_and_resume(void *arg)
+{
+ struct bnxt *bp = arg;
+ int rc;
+
+ bnxt_dev_cleanup(bp);
+
+ bnxt_wait_for_device_shutdown(bp);
+
+ rc = rte_eal_alarm_set(US_PER_MS * bp->fw_reset_min_msecs,
+ bnxt_dev_recover, (void *)bp);
+ if (rc)
+ PMD_DRV_LOG(ERR, "Error setting recovery alarm");
+}
+
+uint32_t bnxt_read_fw_status_reg(struct bnxt *bp, uint32_t index)
+{
+ struct bnxt_error_recovery_info *info = bp->recovery_info;
+ uint32_t reg = info->status_regs[index];
+ uint32_t type, offset, val = 0;
+
+ type = BNXT_FW_STATUS_REG_TYPE(reg);
+ offset = BNXT_FW_STATUS_REG_OFF(reg);
+
+ switch (type) {
+ case BNXT_FW_STATUS_REG_TYPE_CFG:
+ rte_pci_read_config(bp->pdev, &val, sizeof(val), offset);
+ break;
+ case BNXT_FW_STATUS_REG_TYPE_GRC:
+ offset = info->mapped_status_regs[index];
+ /* FALLTHROUGH */
+ case BNXT_FW_STATUS_REG_TYPE_BAR0:
+ val = rte_le_to_cpu_32(rte_read32((uint8_t *)bp->bar0 +
+ offset));
+ break;
+ }
+
+ return val;
+}
+
+static int bnxt_fw_reset_all(struct bnxt *bp)
+{
+ struct bnxt_error_recovery_info *info = bp->recovery_info;
+ uint32_t i;
+ int rc = 0;
+
+ if (info->flags & BNXT_FLAG_ERROR_RECOVERY_HOST) {
+ /* Reset through master function driver */
+ for (i = 0; i < info->reg_array_cnt; i++)
+ bnxt_write_fw_reset_reg(bp, i);
+ /* Wait for time specified by FW after triggering reset */
+ rte_delay_ms(info->master_func_wait_period_after_reset);
+ } else if (info->flags & BNXT_FLAG_ERROR_RECOVERY_CO_CPU) {
+ /* Reset with the help of Kong processor */
+ rc = bnxt_hwrm_fw_reset(bp);
+ if (rc)
+ PMD_DRV_LOG(ERR, "Failed to reset FW\n");
+ }
+
+ return rc;
+}
+
+static void bnxt_fw_reset_cb(void *arg)
+{
+ struct bnxt *bp = arg;
+ struct bnxt_error_recovery_info *info = bp->recovery_info;
+ int rc = 0;
+
+ /* Only Master function can do FW reset */
+ if (bnxt_is_master_func(bp) &&
+ bnxt_is_recovery_enabled(bp)) {
+ rc = bnxt_fw_reset_all(bp);
+ if (rc) {
+ PMD_DRV_LOG(ERR, "Adapter recovery failed\n");
+ return;
+ }
+ }
+
+ /* if recovery method is ERROR_RECOVERY_CO_CPU, KONG will send
+ * EXCEPTION_FATAL_ASYNC event to all the functions
+ * (including MASTER FUNC). After receiving this Async, all the active
+ * drivers should treat this case as FW initiated recovery
+ */
+ if (info->flags & BNXT_FLAG_ERROR_RECOVERY_HOST) {
+ bp->fw_reset_min_msecs = BNXT_MIN_FW_READY_TIMEOUT;
+ bp->fw_reset_max_msecs = BNXT_MAX_FW_RESET_TIMEOUT;
+
+ /* To recover from error */
+ rte_eal_alarm_set(US_PER_MS, bnxt_dev_reset_and_resume,
+ (void *)bp);
+ }
+}
+
+/* Driver should poll FW heartbeat, reset_counter with the frequency
+ * advertised by FW in HWRM_ERROR_RECOVERY_QCFG.
+ * When the driver detects heartbeat stop or change in reset_counter,
+ * it has to trigger a reset to recover from the error condition.
+ * A “master PF” is the function who will have the privilege to
+ * initiate the chimp reset. The master PF will be elected by the
+ * firmware and will be notified through async message.
+ */
+static void bnxt_check_fw_health(void *arg)
+{
+ struct bnxt *bp = arg;
+ struct bnxt_error_recovery_info *info = bp->recovery_info;
+ uint32_t val = 0, wait_msec;
+
+ if (!info || !bnxt_is_recovery_enabled(bp) ||
+ is_bnxt_in_error(bp))
+ return;
+
+ val = bnxt_read_fw_status_reg(bp, BNXT_FW_HEARTBEAT_CNT_REG);
+ if (val == info->last_heart_beat)
+ goto reset;
+
+ info->last_heart_beat = val;
+
+ val = bnxt_read_fw_status_reg(bp, BNXT_FW_RECOVERY_CNT_REG);
+ if (val != info->last_reset_counter)
+ goto reset;
+
+ info->last_reset_counter = val;
+
+ rte_eal_alarm_set(US_PER_MS * info->driver_polling_freq,
+ bnxt_check_fw_health, (void *)bp);
+
+ return;
+reset:
+ /* Stop DMA to/from device */
+ bp->flags |= BNXT_FLAG_FATAL_ERROR;
+ bp->flags |= BNXT_FLAG_FW_RESET;
+
+ PMD_DRV_LOG(ERR, "Detected FW dead condition\n");
+
+ if (bnxt_is_master_func(bp))
+ wait_msec = info->master_func_wait_period;
+ else
+ wait_msec = info->normal_func_wait_period;
+
+ rte_eal_alarm_set(US_PER_MS * wait_msec,
+ bnxt_fw_reset_cb, (void *)bp);
+}
+
+void bnxt_schedule_fw_health_check(struct bnxt *bp)
+{
+ uint32_t polling_freq;
+
+ pthread_mutex_lock(&bp->health_check_lock);
+
+ if (!bnxt_is_recovery_enabled(bp))
+ goto done;
+
+ if (bp->flags & BNXT_FLAG_FW_HEALTH_CHECK_SCHEDULED)
+ goto done;
+
+ polling_freq = bp->recovery_info->driver_polling_freq;
+
+ rte_eal_alarm_set(US_PER_MS * polling_freq,
+ bnxt_check_fw_health, (void *)bp);
+ bp->flags |= BNXT_FLAG_FW_HEALTH_CHECK_SCHEDULED;
+
+done:
+ pthread_mutex_unlock(&bp->health_check_lock);
+}
+
+static void bnxt_cancel_fw_health_check(struct bnxt *bp)
+{
+ if (!bnxt_is_recovery_enabled(bp))
+ return;
+
+ rte_eal_alarm_cancel(bnxt_check_fw_health, (void *)bp);
+ bp->flags &= ~BNXT_FLAG_FW_HEALTH_CHECK_SCHEDULED;
+}
+
+static bool bnxt_vf_pciid(uint16_t device_id)
+{
+ switch (device_id) {
+ case BROADCOM_DEV_ID_57304_VF:
+ case BROADCOM_DEV_ID_57406_VF:
+ case BROADCOM_DEV_ID_5731X_VF:
+ case BROADCOM_DEV_ID_5741X_VF:
+ case BROADCOM_DEV_ID_57414_VF:
+ case BROADCOM_DEV_ID_STRATUS_NIC_VF1:
+ case BROADCOM_DEV_ID_STRATUS_NIC_VF2:
+ case BROADCOM_DEV_ID_58802_VF:
+ case BROADCOM_DEV_ID_57500_VF1:
+ case BROADCOM_DEV_ID_57500_VF2:
+ /* FALLTHROUGH */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool bnxt_thor_device(uint16_t device_id)
+{
+ switch (device_id) {
+ case BROADCOM_DEV_ID_57508:
+ case BROADCOM_DEV_ID_57504:
+ case BROADCOM_DEV_ID_57502:
+ case BROADCOM_DEV_ID_57508_MF1:
+ case BROADCOM_DEV_ID_57504_MF1:
+ case BROADCOM_DEV_ID_57502_MF1:
+ case BROADCOM_DEV_ID_57508_MF2:
+ case BROADCOM_DEV_ID_57504_MF2:
+ case BROADCOM_DEV_ID_57502_MF2:
+ case BROADCOM_DEV_ID_57500_VF1:
+ case BROADCOM_DEV_ID_57500_VF2:
+ /* FALLTHROUGH */
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool bnxt_stratus_device(struct bnxt *bp)
+{
+ uint16_t device_id = bp->pdev->id.device_id;
+
+ switch (device_id) {
+ case BROADCOM_DEV_ID_STRATUS_NIC:
+ case BROADCOM_DEV_ID_STRATUS_NIC_VF1:
+ case BROADCOM_DEV_ID_STRATUS_NIC_VF2:
+ /* FALLTHROUGH */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int bnxt_init_board(struct rte_eth_dev *eth_dev)
+{
+ struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(eth_dev);
+ struct bnxt *bp = eth_dev->data->dev_private;
+
+ /* enable device (incl. PCI PM wakeup), and bus-mastering */
+ bp->bar0 = (void *)pci_dev->mem_resource[0].addr;
+ bp->doorbell_base = (void *)pci_dev->mem_resource[2].addr;
+ if (!bp->bar0 || !bp->doorbell_base) {
+ PMD_DRV_LOG(ERR, "Unable to access Hardware\n");
+ return -ENODEV;
+ }
+
+ bp->eth_dev = eth_dev;
+ bp->pdev = pci_dev;
+
+ return 0;
+}
+
+static int bnxt_alloc_ctx_mem_blk(struct bnxt *bp,
+ struct bnxt_ctx_pg_info *ctx_pg,
+ uint32_t mem_size,
+ const char *suffix,
+ uint16_t idx)
+{
+ struct bnxt_ring_mem_info *rmem = &ctx_pg->ring_mem;
+ const struct rte_memzone *mz = NULL;
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ rte_iova_t mz_phys_addr;
+ uint64_t valid_bits = 0;
+ uint32_t sz;
+ int i;
+
+ if (!mem_size)
+ return 0;
+
+ rmem->nr_pages = RTE_ALIGN_MUL_CEIL(mem_size, BNXT_PAGE_SIZE) /
+ BNXT_PAGE_SIZE;
+ rmem->page_size = BNXT_PAGE_SIZE;
+ rmem->pg_arr = ctx_pg->ctx_pg_arr;
+ rmem->dma_arr = ctx_pg->ctx_dma_arr;
+ rmem->flags = BNXT_RMEM_VALID_PTE_FLAG;
+
+ valid_bits = PTU_PTE_VALID;
+
+ if (rmem->nr_pages > 1) {
+ snprintf(mz_name, RTE_MEMZONE_NAMESIZE,
+ "bnxt_ctx_pg_tbl%s_%x_%d",
+ suffix, idx, bp->eth_dev->data->port_id);
+ mz_name[RTE_MEMZONE_NAMESIZE - 1] = 0;
+ mz = rte_memzone_lookup(mz_name);
+ if (!mz) {
+ mz = rte_memzone_reserve_aligned(mz_name,
+ rmem->nr_pages * 8,
+ SOCKET_ID_ANY,
+ RTE_MEMZONE_2MB |
+ RTE_MEMZONE_SIZE_HINT_ONLY |
+ RTE_MEMZONE_IOVA_CONTIG,
+ BNXT_PAGE_SIZE);
+ if (mz == NULL)
+ return -ENOMEM;
+ }
+
+ memset(mz->addr, 0, mz->len);
+ mz_phys_addr = mz->iova;
+
+ rmem->pg_tbl = mz->addr;
+ rmem->pg_tbl_map = mz_phys_addr;
+ rmem->pg_tbl_mz = mz;
+ }
+
+ snprintf(mz_name, RTE_MEMZONE_NAMESIZE, "bnxt_ctx_%s_%x_%d",
+ suffix, idx, bp->eth_dev->data->port_id);
+ mz = rte_memzone_lookup(mz_name);
+ if (!mz) {
+ mz = rte_memzone_reserve_aligned(mz_name,
+ mem_size,
+ SOCKET_ID_ANY,
+ RTE_MEMZONE_1GB |
+ RTE_MEMZONE_SIZE_HINT_ONLY |
+ RTE_MEMZONE_IOVA_CONTIG,
+ BNXT_PAGE_SIZE);
+ if (mz == NULL)
+ return -ENOMEM;
+ }
+
+ memset(mz->addr, 0, mz->len);
+ mz_phys_addr = mz->iova;
+
+ for (sz = 0, i = 0; sz < mem_size; sz += BNXT_PAGE_SIZE, i++) {
+ rmem->pg_arr[i] = ((char *)mz->addr) + sz;
+ rmem->dma_arr[i] = mz_phys_addr + sz;
+
+ if (rmem->nr_pages > 1) {
+ if (i == rmem->nr_pages - 2 &&
+ (rmem->flags & BNXT_RMEM_RING_PTE_FLAG))
+ valid_bits |= PTU_PTE_NEXT_TO_LAST;
+ else if (i == rmem->nr_pages - 1 &&
+ (rmem->flags & BNXT_RMEM_RING_PTE_FLAG))
+ valid_bits |= PTU_PTE_LAST;
+
+ rmem->pg_tbl[i] = rte_cpu_to_le_64(rmem->dma_arr[i] |
+ valid_bits);
+ }
+ }
+
+ rmem->mz = mz;
+ if (rmem->vmem_size)
+ rmem->vmem = (void **)mz->addr;
+ rmem->dma_arr[0] = mz_phys_addr;
+ return 0;
+}
+
+static void bnxt_free_ctx_mem(struct bnxt *bp)
+{
+ int i;
+
+ if (!bp->ctx || !(bp->ctx->flags & BNXT_CTX_FLAG_INITED))
+ return;
+
+ bp->ctx->flags &= ~BNXT_CTX_FLAG_INITED;
+ rte_memzone_free(bp->ctx->qp_mem.ring_mem.mz);
+ rte_memzone_free(bp->ctx->srq_mem.ring_mem.mz);
+ rte_memzone_free(bp->ctx->cq_mem.ring_mem.mz);
+ rte_memzone_free(bp->ctx->vnic_mem.ring_mem.mz);
+ rte_memzone_free(bp->ctx->stat_mem.ring_mem.mz);
+ rte_memzone_free(bp->ctx->qp_mem.ring_mem.pg_tbl_mz);
+ rte_memzone_free(bp->ctx->srq_mem.ring_mem.pg_tbl_mz);
+ rte_memzone_free(bp->ctx->cq_mem.ring_mem.pg_tbl_mz);
+ rte_memzone_free(bp->ctx->vnic_mem.ring_mem.pg_tbl_mz);
+ rte_memzone_free(bp->ctx->stat_mem.ring_mem.pg_tbl_mz);
+
+ for (i = 0; i < bp->ctx->tqm_fp_rings_count + 1; i++) {
+ if (bp->ctx->tqm_mem[i])
+ rte_memzone_free(bp->ctx->tqm_mem[i]->ring_mem.mz);
+ }
+
+ rte_free(bp->ctx);
+ bp->ctx = NULL;
+}
+
+#define bnxt_roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
+
+#define min_t(type, x, y) ({ \
+ type __min1 = (x); \
+ type __min2 = (y); \
+ __min1 < __min2 ? __min1 : __min2; })
+
+#define max_t(type, x, y) ({ \
+ type __max1 = (x); \
+ type __max2 = (y); \
+ __max1 > __max2 ? __max1 : __max2; })
+
+#define clamp_t(type, _x, min, max) min_t(type, max_t(type, _x, min), max)
+
+int bnxt_alloc_ctx_mem(struct bnxt *bp)
+{
+ struct bnxt_ctx_pg_info *ctx_pg;
+ struct bnxt_ctx_mem_info *ctx;
+ uint32_t mem_size, ena, entries;
+ uint32_t entries_sp, min;
+ int i, rc;
+
+ rc = bnxt_hwrm_func_backing_store_qcaps(bp);
+ if (rc) {
+ PMD_DRV_LOG(ERR, "Query context mem capability failed\n");
+ return rc;
+ }
+ ctx = bp->ctx;
+ if (!ctx || (ctx->flags & BNXT_CTX_FLAG_INITED))
+ return 0;
+
+ ctx_pg = &ctx->qp_mem;
+ ctx_pg->entries = ctx->qp_min_qp1_entries + ctx->qp_max_l2_entries;
+ mem_size = ctx->qp_entry_size * ctx_pg->entries;
+ rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg, mem_size, "qp_mem", 0);
+ if (rc)
+ return rc;
+
+ ctx_pg = &ctx->srq_mem;
+ ctx_pg->entries = ctx->srq_max_l2_entries;
+ mem_size = ctx->srq_entry_size * ctx_pg->entries;
+ rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg, mem_size, "srq_mem", 0);
+ if (rc)
+ return rc;
+
+ ctx_pg = &ctx->cq_mem;
+ ctx_pg->entries = ctx->cq_max_l2_entries;
+ mem_size = ctx->cq_entry_size * ctx_pg->entries;
+ rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg, mem_size, "cq_mem", 0);
+ if (rc)
+ return rc;
+
+ ctx_pg = &ctx->vnic_mem;
+ ctx_pg->entries = ctx->vnic_max_vnic_entries +
+ ctx->vnic_max_ring_table_entries;
+ mem_size = ctx->vnic_entry_size * ctx_pg->entries;
+ rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg, mem_size, "vnic_mem", 0);
+ if (rc)
+ return rc;
+
+ ctx_pg = &ctx->stat_mem;
+ ctx_pg->entries = ctx->stat_max_entries;
+ mem_size = ctx->stat_entry_size * ctx_pg->entries;
+ rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg, mem_size, "stat_mem", 0);
+ if (rc)
+ return rc;
+
+ min = ctx->tqm_min_entries_per_ring;
+
+ entries_sp = ctx->qp_max_l2_entries +
+ ctx->vnic_max_vnic_entries +
+ 2 * ctx->qp_min_qp1_entries + min;
+ entries_sp = bnxt_roundup(entries_sp, ctx->tqm_entries_multiple);
+
+ entries = ctx->qp_max_l2_entries + ctx->qp_min_qp1_entries;
+ entries = bnxt_roundup(entries, ctx->tqm_entries_multiple);
+ entries = clamp_t(uint32_t, entries, min,
+ ctx->tqm_max_entries_per_ring);
+ for (i = 0, ena = 0; i < ctx->tqm_fp_rings_count + 1; i++) {
+ ctx_pg = ctx->tqm_mem[i];
+ ctx_pg->entries = i ? entries : entries_sp;
+ mem_size = ctx->tqm_entry_size * ctx_pg->entries;
+ rc = bnxt_alloc_ctx_mem_blk(bp, ctx_pg, mem_size, "tqm_mem", i);
+ if (rc)
+ return rc;
+ ena |= HWRM_FUNC_BACKING_STORE_CFG_INPUT_ENABLES_TQM_SP << i;
+ }
+
+ ena |= FUNC_BACKING_STORE_CFG_INPUT_DFLT_ENABLES;
+ rc = bnxt_hwrm_func_backing_store_cfg(bp, ena);
+ if (rc)
+ PMD_DRV_LOG(ERR,
+ "Failed to configure context mem: rc = %d\n", rc);
+ else
+ ctx->flags |= BNXT_CTX_FLAG_INITED;
+
+ return rc;
+}
+
+static int bnxt_alloc_stats_mem(struct bnxt *bp)
+{
+ struct rte_pci_device *pci_dev = bp->pdev;
+ char mz_name[RTE_MEMZONE_NAMESIZE];
+ const struct rte_memzone *mz = NULL;
+ uint32_t total_alloc_len;
+ rte_iova_t mz_phys_addr;
+
+ if (pci_dev->id.device_id == BROADCOM_DEV_ID_NS2)
+ return 0;
+
+ snprintf(mz_name, RTE_MEMZONE_NAMESIZE,
+ "bnxt_" PCI_PRI_FMT "-%s", pci_dev->addr.domain,
+ pci_dev->addr.bus, pci_dev->addr.devid,
+ pci_dev->addr.function, "rx_port_stats");
+ mz_name[RTE_MEMZONE_NAMESIZE - 1] = 0;
+ mz = rte_memzone_lookup(mz_name);
+ total_alloc_len =
+ RTE_CACHE_LINE_ROUNDUP(sizeof(struct rx_port_stats) +
+ sizeof(struct rx_port_stats_ext) + 512);
+ if (!mz) {
+ mz = rte_memzone_reserve(mz_name, total_alloc_len,
+ SOCKET_ID_ANY,
+ RTE_MEMZONE_2MB |
+ RTE_MEMZONE_SIZE_HINT_ONLY |
+ RTE_MEMZONE_IOVA_CONTIG);
+ if (mz == NULL)
+ return -ENOMEM;
+ }
+ memset(mz->addr, 0, mz->len);
+ mz_phys_addr = mz->iova;
+
+ bp->rx_mem_zone = (const void *)mz;
+ bp->hw_rx_port_stats = mz->addr;
+ bp->hw_rx_port_stats_map = mz_phys_addr;
+
+ snprintf(mz_name, RTE_MEMZONE_NAMESIZE,
+ "bnxt_" PCI_PRI_FMT "-%s", pci_dev->addr.domain,
+ pci_dev->addr.bus, pci_dev->addr.devid,
+ pci_dev->addr.function, "tx_port_stats");
+ mz_name[RTE_MEMZONE_NAMESIZE - 1] = 0;
+ mz = rte_memzone_lookup(mz_name);
+ total_alloc_len =
+ RTE_CACHE_LINE_ROUNDUP(sizeof(struct tx_port_stats) +
+ sizeof(struct tx_port_stats_ext) + 512);
+ if (!mz) {
+ mz = rte_memzone_reserve(mz_name,
+ total_alloc_len,
+ SOCKET_ID_ANY,
+ RTE_MEMZONE_2MB |
+ RTE_MEMZONE_SIZE_HINT_ONLY |
+ RTE_MEMZONE_IOVA_CONTIG);
+ if (mz == NULL)
+ return -ENOMEM;
+ }
+ memset(mz->addr, 0, mz->len);
+ mz_phys_addr = mz->iova;
+
+ bp->tx_mem_zone = (const void *)mz;
+ bp->hw_tx_port_stats = mz->addr;
+ bp->hw_tx_port_stats_map = mz_phys_addr;
+ bp->flags |= BNXT_FLAG_PORT_STATS;
+
+ /* Display extended statistics if FW supports it */
+ if (bp->hwrm_spec_code < HWRM_SPEC_CODE_1_8_4 ||
+ bp->hwrm_spec_code == HWRM_SPEC_CODE_1_9_0 ||
+ !(bp->flags & BNXT_FLAG_EXT_STATS_SUPPORTED))
+ return 0;
+
+ bp->hw_rx_port_stats_ext = (void *)
+ ((uint8_t *)bp->hw_rx_port_stats +
+ sizeof(struct rx_port_stats));
+ bp->hw_rx_port_stats_ext_map = bp->hw_rx_port_stats_map +
+ sizeof(struct rx_port_stats);
+ bp->flags |= BNXT_FLAG_EXT_RX_PORT_STATS;
+
+ if (bp->hwrm_spec_code < HWRM_SPEC_CODE_1_9_2 ||
+ bp->flags & BNXT_FLAG_EXT_STATS_SUPPORTED) {
+ bp->hw_tx_port_stats_ext = (void *)
+ ((uint8_t *)bp->hw_tx_port_stats +
+ sizeof(struct tx_port_stats));
+ bp->hw_tx_port_stats_ext_map =
+ bp->hw_tx_port_stats_map +
+ sizeof(struct tx_port_stats);
+ bp->flags |= BNXT_FLAG_EXT_TX_PORT_STATS;
+ }
+
+ return 0;
+}
+
+static int bnxt_setup_mac_addr(struct rte_eth_dev *eth_dev)
+{
+ struct bnxt *bp = eth_dev->data->dev_private;
+ int rc = 0;
+
+ eth_dev->data->mac_addrs = rte_zmalloc("bnxt_mac_addr_tbl",
+ RTE_ETHER_ADDR_LEN *
+ bp->max_l2_ctx,
+ 0);
+ if (eth_dev->data->mac_addrs == NULL) {
+ PMD_DRV_LOG(ERR, "Failed to alloc MAC addr tbl\n");
+ return -ENOMEM;
+ }
+
+ if (!BNXT_HAS_DFLT_MAC_SET(bp)) {
+ if (BNXT_PF(bp))
+ return -EINVAL;
+
+ /* Generate a random MAC address, if none was assigned by PF */
+ PMD_DRV_LOG(INFO, "VF MAC address not assigned by Host PF\n");
+ bnxt_eth_hw_addr_random(bp->mac_addr);
+ PMD_DRV_LOG(INFO,
+ "Assign random MAC:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ bp->mac_addr[0], bp->mac_addr[1], bp->mac_addr[2],
+ bp->mac_addr[3], bp->mac_addr[4], bp->mac_addr[5]);
+
+ rc = bnxt_hwrm_set_mac(bp);
+ if (rc)
+ return rc;
+ }
+
+ /* Copy the permanent MAC from the FUNC_QCAPS response */
+ memcpy(ð_dev->data->mac_addrs[0], bp->mac_addr, RTE_ETHER_ADDR_LEN);
+
+ return rc;
+}
+
+static int bnxt_restore_dflt_mac(struct bnxt *bp)
+{
+ int rc = 0;
+
+ /* MAC is already configured in FW */
+ if (BNXT_HAS_DFLT_MAC_SET(bp))
+ return 0;
+
+ /* Restore the old MAC configured */
+ rc = bnxt_hwrm_set_mac(bp);
+ if (rc)
+ PMD_DRV_LOG(ERR, "Failed to restore MAC address\n");
+
+ return rc;
+}
+
+static void bnxt_config_vf_req_fwd(struct bnxt *bp)
+{
+ if (!BNXT_PF(bp))
+ return;
+
+ memset(bp->pf->vf_req_fwd, 0, sizeof(bp->pf->vf_req_fwd));
+
+ if (!(bp->fw_cap & BNXT_FW_CAP_LINK_ADMIN))
+ BNXT_HWRM_CMD_TO_FORWARD(HWRM_PORT_PHY_QCFG);
+ BNXT_HWRM_CMD_TO_FORWARD(HWRM_FUNC_CFG);
+ BNXT_HWRM_CMD_TO_FORWARD(HWRM_FUNC_VF_CFG);
+ BNXT_HWRM_CMD_TO_FORWARD(HWRM_CFA_L2_FILTER_ALLOC);
+ BNXT_HWRM_CMD_TO_FORWARD(HWRM_OEM_CMD);
+}
+
+uint16_t
+bnxt_get_svif(uint16_t port_id, bool func_svif,
+ enum bnxt_ulp_intf_type type)
+{
+ struct rte_eth_dev *eth_dev;
+ struct bnxt *bp;
+
+ eth_dev = &rte_eth_devices[port_id];
+ if (BNXT_ETH_DEV_IS_REPRESENTOR(eth_dev)) {
+ struct bnxt_representor *vfr = eth_dev->data->dev_private;
+ if (!vfr)
+ return 0;
+
+ if (type == BNXT_ULP_INTF_TYPE_VF_REP)
+ return vfr->svif;
+
+ eth_dev = vfr->parent_dev;
+ }
+
+ bp = eth_dev->data->dev_private;
+
+ return func_svif ? bp->func_svif : bp->port_svif;
+}
+
+uint16_t
+bnxt_get_vnic_id(uint16_t port, enum bnxt_ulp_intf_type type)
+{
+ struct rte_eth_dev *eth_dev;
+ struct bnxt_vnic_info *vnic;
+ struct bnxt *bp;
+
+ eth_dev = &rte_eth_devices[port];
+ if (BNXT_ETH_DEV_IS_REPRESENTOR(eth_dev)) {
+ struct bnxt_representor *vfr = eth_dev->data->dev_private;
+ if (!vfr)
+ return 0;
+
+ if (type == BNXT_ULP_INTF_TYPE_VF_REP)
+ return vfr->dflt_vnic_id;
+
+ eth_dev = vfr->parent_dev;
+ }
+
+ bp = eth_dev->data->dev_private;
+
+ vnic = BNXT_GET_DEFAULT_VNIC(bp);
+
+ return vnic->fw_vnic_id;
+}
+
+uint16_t
+bnxt_get_fw_func_id(uint16_t port, enum bnxt_ulp_intf_type type)
+{
+ struct rte_eth_dev *eth_dev;
+ struct bnxt *bp;
+
+ eth_dev = &rte_eth_devices[port];
+ if (BNXT_ETH_DEV_IS_REPRESENTOR(eth_dev)) {
+ struct bnxt_representor *vfr = eth_dev->data->dev_private;
+ if (!vfr)
+ return 0;
+
+ if (type == BNXT_ULP_INTF_TYPE_VF_REP)
+ return vfr->fw_fid;
+
+ eth_dev = vfr->parent_dev;
+ }
+
+ bp = eth_dev->data->dev_private;
+
+ return bp->fw_fid;
+}
+
+enum bnxt_ulp_intf_type
+bnxt_get_interface_type(uint16_t port)
+{
+ struct rte_eth_dev *eth_dev;
+ struct bnxt *bp;
+
+ eth_dev = &rte_eth_devices[port];
+ if (BNXT_ETH_DEV_IS_REPRESENTOR(eth_dev))
+ return BNXT_ULP_INTF_TYPE_VF_REP;
+
+ bp = eth_dev->data->dev_private;
+ if (BNXT_PF(bp))
+ return BNXT_ULP_INTF_TYPE_PF;
+ else if (BNXT_VF_IS_TRUSTED(bp))
+ return BNXT_ULP_INTF_TYPE_TRUSTED_VF;
+ else if (BNXT_VF(bp))
+ return BNXT_ULP_INTF_TYPE_VF;
+
+ return BNXT_ULP_INTF_TYPE_INVALID;
+}
+
+uint16_t
+bnxt_get_phy_port_id(uint16_t port_id)
+{
+ struct bnxt_representor *vfr;
+ struct rte_eth_dev *eth_dev;
+ struct bnxt *bp;
+
+ eth_dev = &rte_eth_devices[port_id];
+ if (BNXT_ETH_DEV_IS_REPRESENTOR(eth_dev)) {
+ vfr = eth_dev->data->dev_private;
+ if (!vfr)
+ return 0;
+
+ eth_dev = vfr->parent_dev;
+ }
+
+ bp = eth_dev->data->dev_private;
+
+ return BNXT_PF(bp) ? bp->pf->port_id : bp->parent->port_id;
+}
+
+uint16_t
+bnxt_get_parif(uint16_t port_id, enum bnxt_ulp_intf_type type)
+{
+ struct rte_eth_dev *eth_dev;
+ struct bnxt *bp;
+
+ eth_dev = &rte_eth_devices[port_id];
+ if (BNXT_ETH_DEV_IS_REPRESENTOR(eth_dev)) {
+ struct bnxt_representor *vfr = eth_dev->data->dev_private;
+ if (!vfr)
+ return 0;
+
+ if (type == BNXT_ULP_INTF_TYPE_VF_REP)
+ return vfr->fw_fid - 1;
+
+ eth_dev = vfr->parent_dev;
+ }
+
+ bp = eth_dev->data->dev_private;
+
+ return BNXT_PF(bp) ? bp->fw_fid - 1 : bp->parent->fid - 1;
+}
+
+uint16_t
+bnxt_get_vport(uint16_t port_id)
+{
+ return (1 << bnxt_get_phy_port_id(port_id));
+}
+
+static void bnxt_alloc_error_recovery_info(struct bnxt *bp)
+{
+ struct bnxt_error_recovery_info *info = bp->recovery_info;
+
+ if (info) {
+ if (!(bp->fw_cap & BNXT_FW_CAP_HCOMM_FW_STATUS))
+ memset(info, 0, sizeof(*info));
+ return;
+ }
+
+ if (!(bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY))
+ return;
+
+ info = rte_zmalloc("bnxt_hwrm_error_recovery_qcfg",
+ sizeof(*info), 0);
+ if (!info)
+ bp->fw_cap &= ~BNXT_FW_CAP_ERROR_RECOVERY;
+
+ bp->recovery_info = info;
+}
+
+static void bnxt_check_fw_status(struct bnxt *bp)
+{
+ uint32_t fw_status;
+
+ if (!(bp->recovery_info &&
+ (bp->fw_cap & BNXT_FW_CAP_HCOMM_FW_STATUS)))
+ return;
+
+ fw_status = bnxt_read_fw_status_reg(bp, BNXT_FW_STATUS_REG);
+ if (fw_status != BNXT_FW_STATUS_HEALTHY)
+ PMD_DRV_LOG(ERR, "Firmware not responding, status: %#x\n",
+ fw_status);
+}
+
+static int bnxt_map_hcomm_fw_status_reg(struct bnxt *bp)
+{
+ struct bnxt_error_recovery_info *info = bp->recovery_info;
+ uint32_t status_loc;
+ uint32_t sig_ver;
+
+ rte_write32(HCOMM_STATUS_STRUCT_LOC, (uint8_t *)bp->bar0 +
+ BNXT_GRCPF_REG_WINDOW_BASE_OUT + 4);
+ sig_ver = rte_le_to_cpu_32(rte_read32((uint8_t *)bp->bar0 +
+ BNXT_GRCP_WINDOW_2_BASE +
+ offsetof(struct hcomm_status,
+ sig_ver)));
+ /* If the signature is absent, then FW does not support this feature */
+ if ((sig_ver & HCOMM_STATUS_SIGNATURE_MASK) !=
+ HCOMM_STATUS_SIGNATURE_VAL)
+ return 0;
+
+ if (!info) {
+ info = rte_zmalloc("bnxt_hwrm_error_recovery_qcfg",
+ sizeof(*info), 0);
+ if (!info)
+ return -ENOMEM;
+ bp->recovery_info = info;
+ } else {
+ memset(info, 0, sizeof(*info));
+ }
+
+ status_loc = rte_le_to_cpu_32(rte_read32((uint8_t *)bp->bar0 +
+ BNXT_GRCP_WINDOW_2_BASE +
+ offsetof(struct hcomm_status,
+ fw_status_loc)));
+
+ /* Only pre-map the FW health status GRC register */
+ if (BNXT_FW_STATUS_REG_TYPE(status_loc) != BNXT_FW_STATUS_REG_TYPE_GRC)
+ return 0;
+
+ info->status_regs[BNXT_FW_STATUS_REG] = status_loc;
+ info->mapped_status_regs[BNXT_FW_STATUS_REG] =
+ BNXT_GRCP_WINDOW_2_BASE + (status_loc & BNXT_GRCP_OFFSET_MASK);
+
+ rte_write32((status_loc & BNXT_GRCP_BASE_MASK), (uint8_t *)bp->bar0 +
+ BNXT_GRCPF_REG_WINDOW_BASE_OUT + 4);
+
+ bp->fw_cap |= BNXT_FW_CAP_HCOMM_FW_STATUS;
+
+ return 0;
+}
+
+static int bnxt_init_fw(struct bnxt *bp)
+{
+ uint16_t mtu;
+ int rc = 0;
+
+ bp->fw_cap = 0;
+
+ rc = bnxt_map_hcomm_fw_status_reg(bp);
+ if (rc)
+ return rc;
+
+ rc = bnxt_hwrm_ver_get(bp, DFLT_HWRM_CMD_TIMEOUT);
+ if (rc) {
+ bnxt_check_fw_status(bp);
+ return rc;
+ }
+
+ rc = bnxt_hwrm_func_reset(bp);
+ if (rc)
+ return -EIO;
+
+ rc = bnxt_hwrm_vnic_qcaps(bp);
+ if (rc)
+ return rc;
+
+ rc = bnxt_hwrm_queue_qportcfg(bp);
+ if (rc)
+ return rc;
+
+ /* Get the MAX capabilities for this function.
+ * This function also allocates context memory for TQM rings and
+ * informs the firmware about this allocated backing store memory.
+ */
+ rc = bnxt_hwrm_func_qcaps(bp);
+ if (rc)
+ return rc;
+
+ rc = bnxt_hwrm_func_qcfg(bp, &mtu);
+ if (rc)
+ return rc;
+
+ bnxt_hwrm_port_mac_qcfg(bp);
+
+ bnxt_hwrm_parent_pf_qcfg(bp);
+
+ bnxt_hwrm_port_phy_qcaps(bp);
+
+ bnxt_alloc_error_recovery_info(bp);
+ /* Get the adapter error recovery support info */
+ rc = bnxt_hwrm_error_recovery_qcfg(bp);
+ if (rc)
+ bp->fw_cap &= ~BNXT_FW_CAP_ERROR_RECOVERY;
+
+ bnxt_hwrm_port_led_qcaps(bp);
+
+ return 0;
+}
+
+static int
+bnxt_init_locks(struct bnxt *bp)
+{
+ int err;
+
+ err = pthread_mutex_init(&bp->flow_lock, NULL);
+ if (err) {
+ PMD_DRV_LOG(ERR, "Unable to initialize flow_lock\n");
+ return err;
+ }
+
+ err = pthread_mutex_init(&bp->def_cp_lock, NULL);
+ if (err)
+ PMD_DRV_LOG(ERR, "Unable to initialize def_cp_lock\n");
+
+ err = pthread_mutex_init(&bp->health_check_lock, NULL);
+ if (err)
+ PMD_DRV_LOG(ERR, "Unable to initialize health_check_lock\n");
+ return err;
+}
+
+static int bnxt_init_resources(struct bnxt *bp, bool reconfig_dev)
+{
+ int rc = 0;
+
+ rc = bnxt_init_fw(bp);
+ if (rc)
+ return rc;
+
+ if (!reconfig_dev) {
+ rc = bnxt_setup_mac_addr(bp->eth_dev);
+ if (rc)
+ return rc;
+ } else {
+ rc = bnxt_restore_dflt_mac(bp);
+ if (rc)
+ return rc;
+ }
+
+ bnxt_config_vf_req_fwd(bp);
+
+ rc = bnxt_hwrm_func_driver_register(bp);
+ if (rc) {
+ PMD_DRV_LOG(ERR, "Failed to register driver");
+ return -EBUSY;
+ }
+
+ if (BNXT_PF(bp)) {
+ if (bp->pdev->max_vfs) {
+ rc = bnxt_hwrm_allocate_vfs(bp, bp->pdev->max_vfs);
+ if (rc) {
+ PMD_DRV_LOG(ERR, "Failed to allocate VFs\n");
+ return rc;
+ }
+ } else {
+ rc = bnxt_hwrm_allocate_pf_only(bp);
+ if (rc) {
+ PMD_DRV_LOG(ERR,
+ "Failed to allocate PF resources");
+ return rc;
+ }
+ }
+ }
+
+ rc = bnxt_alloc_mem(bp, reconfig_dev);
+ if (rc)
+ return rc;
+
+ rc = bnxt_setup_int(bp);
+ if (rc)
+ return rc;
+
+ rc = bnxt_request_int(bp);
+ if (rc)
+ return rc;
+
+ rc = bnxt_init_ctx_mem(bp);
+ if (rc) {
+ PMD_DRV_LOG(ERR, "Failed to init adv_flow_counters\n");
+ return rc;
+ }
+
+ rc = bnxt_init_locks(bp);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int
+bnxt_parse_devarg_truflow(__rte_unused const char *key,
+ const char *value, void *opaque_arg)
+{
+ struct bnxt *bp = opaque_arg;
+ unsigned long truflow;
+ char *end = NULL;
+
+ if (!value || !opaque_arg) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to truflow devargs.\n");
+ return -EINVAL;
+ }
+
+ truflow = strtoul(value, &end, 10);
+ if (end == NULL || *end != '\0' ||
+ (truflow == ULONG_MAX && errno == ERANGE)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to truflow devargs.\n");
+ return -EINVAL;
+ }
+
+ if (BNXT_DEVARG_TRUFLOW_INVALID(truflow)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid value passed to truflow devargs.\n");
+ return -EINVAL;
+ }
+
+ if (truflow) {
+ bp->flags |= BNXT_FLAG_TRUFLOW_EN;
+ PMD_DRV_LOG(INFO, "Host-based truflow feature enabled.\n");
+ } else {
+ bp->flags &= ~BNXT_FLAG_TRUFLOW_EN;
+ PMD_DRV_LOG(INFO, "Host-based truflow feature disabled.\n");
+ }
+
+ return 0;
+}
+
+static int
+bnxt_parse_devarg_flow_xstat(__rte_unused const char *key,
+ const char *value, void *opaque_arg)
+{
+ struct bnxt *bp = opaque_arg;
+ unsigned long flow_xstat;
+ char *end = NULL;
+
+ if (!value || !opaque_arg) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to flow_xstat devarg.\n");
+ return -EINVAL;
+ }
+
+ flow_xstat = strtoul(value, &end, 10);
+ if (end == NULL || *end != '\0' ||
+ (flow_xstat == ULONG_MAX && errno == ERANGE)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to flow_xstat devarg.\n");
+ return -EINVAL;
+ }
+
+ if (BNXT_DEVARG_FLOW_XSTAT_INVALID(flow_xstat)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid value passed to flow_xstat devarg.\n");
+ return -EINVAL;
+ }
+
+ bp->flags |= BNXT_FLAG_FLOW_XSTATS_EN;
+ if (BNXT_FLOW_XSTATS_EN(bp))
+ PMD_DRV_LOG(INFO, "flow_xstat feature enabled.\n");
+
+ return 0;
+}
+
+static int
+bnxt_parse_devarg_max_num_kflows(__rte_unused const char *key,
+ const char *value, void *opaque_arg)
+{
+ struct bnxt *bp = opaque_arg;
+ unsigned long max_num_kflows;
+ char *end = NULL;
+
+ if (!value || !opaque_arg) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to max_num_kflows devarg.\n");
+ return -EINVAL;
+ }
+
+ max_num_kflows = strtoul(value, &end, 10);
+ if (end == NULL || *end != '\0' ||
+ (max_num_kflows == ULONG_MAX && errno == ERANGE)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to max_num_kflows devarg.\n");
+ return -EINVAL;
+ }
+
+ if (bnxt_devarg_max_num_kflow_invalid(max_num_kflows)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid value passed to max_num_kflows devarg.\n");
+ return -EINVAL;
+ }
+
+ bp->max_num_kflows = max_num_kflows;
+ if (bp->max_num_kflows)
+ PMD_DRV_LOG(INFO, "max_num_kflows set as %ldK.\n",
+ max_num_kflows);
+
+ return 0;
+}
+
+static int
+bnxt_parse_devarg_rep_is_pf(__rte_unused const char *key,
+ const char *value, void *opaque_arg)
+{
+ struct bnxt_representor *vfr_bp = opaque_arg;
+ unsigned long rep_is_pf;
+ char *end = NULL;
+
+ if (!value || !opaque_arg) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to rep_is_pf devargs.\n");
+ return -EINVAL;
+ }
+
+ rep_is_pf = strtoul(value, &end, 10);
+ if (end == NULL || *end != '\0' ||
+ (rep_is_pf == ULONG_MAX && errno == ERANGE)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to rep_is_pf devargs.\n");
+ return -EINVAL;
+ }
+
+ if (BNXT_DEVARG_REP_IS_PF_INVALID(rep_is_pf)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid value passed to rep_is_pf devargs.\n");
+ return -EINVAL;
+ }
+
+ vfr_bp->flags |= rep_is_pf;
+ if (BNXT_REP_PF(vfr_bp))
+ PMD_DRV_LOG(INFO, "PF representor\n");
+ else
+ PMD_DRV_LOG(INFO, "VF representor\n");
+
+ return 0;
+}
+
+static int
+bnxt_parse_devarg_rep_based_pf(__rte_unused const char *key,
+ const char *value, void *opaque_arg)
+{
+ struct bnxt_representor *vfr_bp = opaque_arg;
+ unsigned long rep_based_pf;
+ char *end = NULL;
+
+ if (!value || !opaque_arg) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to rep_based_pf "
+ "devargs.\n");
+ return -EINVAL;
+ }
+
+ rep_based_pf = strtoul(value, &end, 10);
+ if (end == NULL || *end != '\0' ||
+ (rep_based_pf == ULONG_MAX && errno == ERANGE)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to rep_based_pf "
+ "devargs.\n");
+ return -EINVAL;
+ }
+
+ if (BNXT_DEVARG_REP_BASED_PF_INVALID(rep_based_pf)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid value passed to rep_based_pf devargs.\n");
+ return -EINVAL;
+ }
+
+ vfr_bp->rep_based_pf = rep_based_pf;
+ vfr_bp->flags |= BNXT_REP_BASED_PF_VALID;
+
+ PMD_DRV_LOG(INFO, "rep-based-pf = %d\n", vfr_bp->rep_based_pf);
+
+ return 0;
+}
+
+static int
+bnxt_parse_devarg_rep_q_r2f(__rte_unused const char *key,
+ const char *value, void *opaque_arg)
+{
+ struct bnxt_representor *vfr_bp = opaque_arg;
+ unsigned long rep_q_r2f;
+ char *end = NULL;
+
+ if (!value || !opaque_arg) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to rep_q_r2f "
+ "devargs.\n");
+ return -EINVAL;
+ }
+
+ rep_q_r2f = strtoul(value, &end, 10);
+ if (end == NULL || *end != '\0' ||
+ (rep_q_r2f == ULONG_MAX && errno == ERANGE)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to rep_q_r2f "
+ "devargs.\n");
+ return -EINVAL;
+ }
+
+ if (BNXT_DEVARG_REP_Q_R2F_INVALID(rep_q_r2f)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid value passed to rep_q_r2f devargs.\n");
+ return -EINVAL;
+ }
+
+ vfr_bp->rep_q_r2f = rep_q_r2f;
+ vfr_bp->flags |= BNXT_REP_Q_R2F_VALID;
+ PMD_DRV_LOG(INFO, "rep-q-r2f = %d\n", vfr_bp->rep_q_r2f);
+
+ return 0;
+}
+
+static int
+bnxt_parse_devarg_rep_q_f2r(__rte_unused const char *key,
+ const char *value, void *opaque_arg)
+{
+ struct bnxt_representor *vfr_bp = opaque_arg;
+ unsigned long rep_q_f2r;
+ char *end = NULL;
+
+ if (!value || !opaque_arg) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to rep_q_f2r "
+ "devargs.\n");
+ return -EINVAL;
+ }
+
+ rep_q_f2r = strtoul(value, &end, 10);
+ if (end == NULL || *end != '\0' ||
+ (rep_q_f2r == ULONG_MAX && errno == ERANGE)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to rep_q_f2r "
+ "devargs.\n");
+ return -EINVAL;
+ }
+
+ if (BNXT_DEVARG_REP_Q_F2R_INVALID(rep_q_f2r)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid value passed to rep_q_f2r devargs.\n");
+ return -EINVAL;
+ }
+
+ vfr_bp->rep_q_f2r = rep_q_f2r;
+ vfr_bp->flags |= BNXT_REP_Q_F2R_VALID;
+ PMD_DRV_LOG(INFO, "rep-q-f2r = %d\n", vfr_bp->rep_q_f2r);
+
+ return 0;
+}
+
+static int
+bnxt_parse_devarg_rep_fc_r2f(__rte_unused const char *key,
+ const char *value, void *opaque_arg)
+{
+ struct bnxt_representor *vfr_bp = opaque_arg;
+ unsigned long rep_fc_r2f;
+ char *end = NULL;
+
+ if (!value || !opaque_arg) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to rep_fc_r2f "
+ "devargs.\n");
+ return -EINVAL;
+ }
+
+ rep_fc_r2f = strtoul(value, &end, 10);
+ if (end == NULL || *end != '\0' ||
+ (rep_fc_r2f == ULONG_MAX && errno == ERANGE)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to rep_fc_r2f "
+ "devargs.\n");
+ return -EINVAL;
+ }
+
+ if (BNXT_DEVARG_REP_FC_R2F_INVALID(rep_fc_r2f)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid value passed to rep_fc_r2f devargs.\n");
+ return -EINVAL;
+ }
+
+ vfr_bp->flags |= BNXT_REP_FC_R2F_VALID;
+ vfr_bp->rep_fc_r2f = rep_fc_r2f;
+ PMD_DRV_LOG(INFO, "rep-fc-r2f = %lu\n", rep_fc_r2f);
+
+ return 0;
+}
+
+static int
+bnxt_parse_devarg_rep_fc_f2r(__rte_unused const char *key,
+ const char *value, void *opaque_arg)
+{
+ struct bnxt_representor *vfr_bp = opaque_arg;
+ unsigned long rep_fc_f2r;
+ char *end = NULL;
+
+ if (!value || !opaque_arg) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to rep_fc_f2r "
+ "devargs.\n");
+ return -EINVAL;
+ }
+
+ rep_fc_f2r = strtoul(value, &end, 10);
+ if (end == NULL || *end != '\0' ||
+ (rep_fc_f2r == ULONG_MAX && errno == ERANGE)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid parameter passed to rep_fc_f2r "
+ "devargs.\n");
+ return -EINVAL;
+ }
+
+ if (BNXT_DEVARG_REP_FC_F2R_INVALID(rep_fc_f2r)) {
+ PMD_DRV_LOG(ERR,
+ "Invalid value passed to rep_fc_f2r devargs.\n");
+ return -EINVAL;
+ }
+
+ vfr_bp->flags |= BNXT_REP_FC_F2R_VALID;
+ vfr_bp->rep_fc_f2r = rep_fc_f2r;
+ PMD_DRV_LOG(INFO, "rep-fc-f2r = %lu\n", rep_fc_f2r);
+
+ return 0;
+}
+
+static void
+bnxt_parse_dev_args(struct bnxt *bp, struct rte_devargs *devargs)
+{
+ struct rte_kvargs *kvlist;
+
+ if (devargs == NULL)
+ return;
+
+ kvlist = rte_kvargs_parse(devargs->args, bnxt_dev_args);
+ if (kvlist == NULL)
+ return;
+
+ /*
+ * Handler for "truflow" devarg.
+ * Invoked as for ex: "-a 0000:00:0d.0,host-based-truflow=1"
+ */
+ rte_kvargs_process(kvlist, BNXT_DEVARG_TRUFLOW,
+ bnxt_parse_devarg_truflow, bp);
+
+ /*
+ * Handler for "flow_xstat" devarg.
+ * Invoked as for ex: "-a 0000:00:0d.0,flow_xstat=1"
+ */
+ rte_kvargs_process(kvlist, BNXT_DEVARG_FLOW_XSTAT,
+ bnxt_parse_devarg_flow_xstat, bp);
+
+ /*
+ * Handler for "max_num_kflows" devarg.
+ * Invoked as for ex: "-a 000:00:0d.0,max_num_kflows=32"
+ */
+ rte_kvargs_process(kvlist, BNXT_DEVARG_MAX_NUM_KFLOWS,
+ bnxt_parse_devarg_max_num_kflows, bp);
+
+ rte_kvargs_free(kvlist);
+}
+
+static int bnxt_alloc_switch_domain(struct bnxt *bp)
+{
+ int rc = 0;
+
+ if (BNXT_PF(bp) || BNXT_VF_IS_TRUSTED(bp)) {
+ rc = rte_eth_switch_domain_alloc(&bp->switch_domain_id);
+ if (rc)
+ PMD_DRV_LOG(ERR,
+ "Failed to alloc switch domain: %d\n", rc);
+ else
+ PMD_DRV_LOG(INFO,
+ "Switch domain allocated %d\n",
+ bp->switch_domain_id);
+ }
+
+ return rc;
+}
+
+static int
+bnxt_dev_init(struct rte_eth_dev *eth_dev, void *params __rte_unused)
+{
+ struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(eth_dev);
+ static int version_printed;
+ struct bnxt *bp;
+ int rc;
+
+ if (version_printed++ == 0)
+ PMD_DRV_LOG(INFO, "%s\n", bnxt_version);
+
+ eth_dev->dev_ops = &bnxt_dev_ops;
+ eth_dev->rx_queue_count = bnxt_rx_queue_count_op;
+ eth_dev->rx_descriptor_status = bnxt_rx_descriptor_status_op;
+ eth_dev->tx_descriptor_status = bnxt_tx_descriptor_status_op;
+ eth_dev->rx_pkt_burst = &bnxt_recv_pkts;
+ eth_dev->tx_pkt_burst = &bnxt_xmit_pkts;
+
+ /*
+ * For secondary processes, we don't initialise any further
+ * as primary has already done this work.
+ */
+ if (rte_eal_process_type() != RTE_PROC_PRIMARY)
+ return 0;
+
+ rte_eth_copy_pci_info(eth_dev, pci_dev);
+ eth_dev->data->dev_flags |= RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
+
+ bp = eth_dev->data->dev_private;
+
+ /* Parse dev arguments passed on when starting the DPDK application. */
+ bnxt_parse_dev_args(bp, pci_dev->device.devargs);
+
+ bp->flags &= ~BNXT_FLAG_RX_VECTOR_PKT_MODE;
+
+ if (bnxt_vf_pciid(pci_dev->id.device_id))
+ bp->flags |= BNXT_FLAG_VF;
+
+ if (bnxt_thor_device(pci_dev->id.device_id))
+ bp->flags |= BNXT_FLAG_THOR_CHIP;
+
+ if (pci_dev->id.device_id == BROADCOM_DEV_ID_58802 ||
+ pci_dev->id.device_id == BROADCOM_DEV_ID_58804 ||
+ pci_dev->id.device_id == BROADCOM_DEV_ID_58808 ||
+ pci_dev->id.device_id == BROADCOM_DEV_ID_58802_VF)
+ bp->flags |= BNXT_FLAG_STINGRAY;
+
+ if (BNXT_TRUFLOW_EN(bp)) {
+ /* extra mbuf field is required to store CFA code from mark */
+ static const struct rte_mbuf_dynfield bnxt_cfa_code_dynfield_desc = {
+ .name = RTE_PMD_BNXT_CFA_CODE_DYNFIELD_NAME,
+ .size = sizeof(bnxt_cfa_code_dynfield_t),
+ .align = __alignof__(bnxt_cfa_code_dynfield_t),
+ };
+ bnxt_cfa_code_dynfield_offset =
+ rte_mbuf_dynfield_register(&bnxt_cfa_code_dynfield_desc);
+ if (bnxt_cfa_code_dynfield_offset < 0) {
+ PMD_DRV_LOG(ERR,
+ "Failed to register mbuf field for TruFlow mark\n");
+ return -rte_errno;
+ }
+ }
+
+ rc = bnxt_init_board(eth_dev);
+ if (rc) {
+ PMD_DRV_LOG(ERR,
+ "Failed to initialize board rc: %x\n", rc);
+ return rc;
+ }
+
+ rc = bnxt_alloc_pf_info(bp);
+ if (rc)
+ goto error_free;
+
+ rc = bnxt_alloc_link_info(bp);
+ if (rc)
+ goto error_free;
+
+ rc = bnxt_alloc_parent_info(bp);
+ if (rc)
+ goto error_free;
+
+ rc = bnxt_alloc_hwrm_resources(bp);
+ if (rc) {
+ PMD_DRV_LOG(ERR,
+ "Failed to allocate hwrm resource rc: %x\n", rc);
+ goto error_free;
+ }
+ rc = bnxt_alloc_leds_info(bp);
+ if (rc)
+ goto error_free;
+
+ rc = bnxt_alloc_cos_queues(bp);
+ if (rc)
+ goto error_free;
+
+ rc = bnxt_init_resources(bp, false);
+ if (rc)
+ goto error_free;
+
+ rc = bnxt_alloc_stats_mem(bp);
+ if (rc)
+ goto error_free;
+
+ bnxt_alloc_switch_domain(bp);
+
+ PMD_DRV_LOG(INFO,
+ DRV_MODULE_NAME "found at mem %" PRIX64 ", node addr %pM\n",
+ pci_dev->mem_resource[0].phys_addr,
+ pci_dev->mem_resource[0].addr);
+
+ return 0;
+
+error_free:
+ bnxt_dev_uninit(eth_dev);
+ return rc;