// SPDX-License-Identifier: GPL-2.0 /* Marvell RVU representor driver * * Copyright (C) 2024 Marvell. * */ #include #include #include #include #include #include "otx2_common.h" #include "cn10k.h" #include "otx2_reg.h" #include "rep.h" #define DRV_NAME "rvu_rep" #define DRV_STRING "Marvell RVU Representor Driver" static const struct pci_device_id rvu_rep_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_RVU_REP) }, { } }; MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION(DRV_STRING); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, rvu_rep_id_table); static int rvu_rep_notify_pfvf(struct otx2_nic *priv, u16 event, struct rep_event *data); static int rvu_rep_mcam_flow_init(struct rep_dev *rep) { struct npc_mcam_alloc_entry_req *req; struct npc_mcam_alloc_entry_rsp *rsp; struct otx2_nic *priv = rep->mdev; int ent, allocated = 0; int count; rep->flow_cfg = kcalloc(1, sizeof(struct otx2_flow_config), GFP_KERNEL); if (!rep->flow_cfg) return -ENOMEM; count = OTX2_DEFAULT_FLOWCOUNT; rep->flow_cfg->flow_ent = kcalloc(count, sizeof(u16), GFP_KERNEL); if (!rep->flow_cfg->flow_ent) return -ENOMEM; while (allocated < count) { req = otx2_mbox_alloc_msg_npc_mcam_alloc_entry(&priv->mbox); if (!req) goto exit; req->hdr.pcifunc = rep->pcifunc; req->contig = false; req->ref_entry = 0; req->count = (count - allocated) > NPC_MAX_NONCONTIG_ENTRIES ? NPC_MAX_NONCONTIG_ENTRIES : count - allocated; if (otx2_sync_mbox_msg(&priv->mbox)) goto exit; rsp = (struct npc_mcam_alloc_entry_rsp *)otx2_mbox_get_rsp (&priv->mbox.mbox, 0, &req->hdr); for (ent = 0; ent < rsp->count; ent++) rep->flow_cfg->flow_ent[ent + allocated] = rsp->entry_list[ent]; allocated += rsp->count; if (rsp->count != req->count) break; } exit: /* Multiple MCAM entry alloc requests could result in non-sequential * MCAM entries in the flow_ent[] array. Sort them in an ascending * order, otherwise user installed ntuple filter index and MCAM entry * index will not be in sync. */ if (allocated) sort(&rep->flow_cfg->flow_ent[0], allocated, sizeof(rep->flow_cfg->flow_ent[0]), mcam_entry_cmp, NULL); mutex_unlock(&priv->mbox.lock); rep->flow_cfg->max_flows = allocated; if (allocated) { rep->flags |= OTX2_FLAG_MCAM_ENTRIES_ALLOC; rep->flags |= OTX2_FLAG_NTUPLE_SUPPORT; rep->flags |= OTX2_FLAG_TC_FLOWER_SUPPORT; } INIT_LIST_HEAD(&rep->flow_cfg->flow_list); INIT_LIST_HEAD(&rep->flow_cfg->flow_list_tc); return 0; } static int rvu_rep_setup_tc_cb(enum tc_setup_type type, void *type_data, void *cb_priv) { struct rep_dev *rep = cb_priv; struct otx2_nic *priv = rep->mdev; if (!(rep->flags & RVU_REP_VF_INITIALIZED)) return -EINVAL; if (!(rep->flags & OTX2_FLAG_TC_FLOWER_SUPPORT)) rvu_rep_mcam_flow_init(rep); priv->netdev = rep->netdev; priv->flags = rep->flags; priv->pcifunc = rep->pcifunc; priv->flow_cfg = rep->flow_cfg; switch (type) { case TC_SETUP_CLSFLOWER: return otx2_setup_tc_cls_flower(priv, type_data); default: return -EOPNOTSUPP; } } static LIST_HEAD(rvu_rep_block_cb_list); static int rvu_rep_setup_tc(struct net_device *netdev, enum tc_setup_type type, void *type_data) { struct rvu_rep *rep = netdev_priv(netdev); switch (type) { case TC_SETUP_BLOCK: return flow_block_cb_setup_simple(type_data, &rvu_rep_block_cb_list, rvu_rep_setup_tc_cb, rep, rep, true); default: return -EOPNOTSUPP; } } static int rvu_rep_sp_stats64(const struct net_device *dev, struct rtnl_link_stats64 *stats) { struct rep_dev *rep = netdev_priv(dev); struct otx2_nic *priv = rep->mdev; struct otx2_rcv_queue *rq; struct otx2_snd_queue *sq; u16 qidx = rep->rep_id; otx2_update_rq_stats(priv, qidx); rq = &priv->qset.rq[qidx]; otx2_update_sq_stats(priv, qidx); sq = &priv->qset.sq[qidx]; stats->tx_bytes = sq->stats.bytes; stats->tx_packets = sq->stats.pkts; stats->rx_bytes = rq->stats.bytes; stats->rx_packets = rq->stats.pkts; return 0; } static bool rvu_rep_has_offload_stats(const struct net_device *dev, int attr_id) { return attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT; } static int rvu_rep_get_offload_stats(int attr_id, const struct net_device *dev, void *sp) { if (attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT) return rvu_rep_sp_stats64(dev, (struct rtnl_link_stats64 *)sp); return -EINVAL; } static int rvu_rep_dl_port_fn_hw_addr_get(struct devlink_port *port, u8 *hw_addr, int *hw_addr_len, struct netlink_ext_ack *extack) { struct rep_dev *rep = container_of(port, struct rep_dev, dl_port); ether_addr_copy(hw_addr, rep->mac); *hw_addr_len = ETH_ALEN; return 0; } static int rvu_rep_dl_port_fn_hw_addr_set(struct devlink_port *port, const u8 *hw_addr, int hw_addr_len, struct netlink_ext_ack *extack) { struct rep_dev *rep = container_of(port, struct rep_dev, dl_port); struct otx2_nic *priv = rep->mdev; struct rep_event evt = {0}; eth_hw_addr_set(rep->netdev, hw_addr); ether_addr_copy(rep->mac, hw_addr); ether_addr_copy(evt.evt_data.mac, hw_addr); evt.pcifunc = rep->pcifunc; rvu_rep_notify_pfvf(priv, RVU_EVENT_MAC_ADDR_CHANGE, &evt); return 0; } static const struct devlink_port_ops rvu_rep_dl_port_ops = { .port_fn_hw_addr_get = rvu_rep_dl_port_fn_hw_addr_get, .port_fn_hw_addr_set = rvu_rep_dl_port_fn_hw_addr_set, }; static void rvu_rep_devlink_set_switch_id(struct otx2_nic *priv, struct netdev_phys_item_id *ppid) { struct pci_dev *pdev = priv->pdev; u64 id; id = pci_get_dsn(pdev); ppid->id_len = sizeof(id); put_unaligned_be64(id, &ppid->id); } static void rvu_rep_devlink_port_unregister(struct rep_dev *rep) { devlink_port_unregister(&rep->dl_port); } static int rvu_rep_devlink_port_register(struct rep_dev *rep) { struct devlink_port_attrs attrs = {}; struct otx2_nic *priv = rep->mdev; struct devlink *dl = priv->dl->dl; int err; if (!(rep->pcifunc & RVU_PFVF_FUNC_MASK)) { attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; attrs.phys.port_number = rvu_get_pf(rep->pcifunc); } else { attrs.flavour = DEVLINK_PORT_FLAVOUR_PCI_VF; attrs.pci_vf.pf = rvu_get_pf(rep->pcifunc); attrs.pci_vf.vf = rep->pcifunc & RVU_PFVF_FUNC_MASK; } rvu_rep_devlink_set_switch_id(priv, &attrs.switch_id); devlink_port_attrs_set(&rep->dl_port, &attrs); err = devl_port_register_with_ops(dl, &rep->dl_port, rep->rep_id, &rvu_rep_dl_port_ops); if (err) { dev_err(rep->mdev->dev, "devlink_port_register failed: %d\n", err); return err; } return 0; } static int rvu_rep_get_repid(struct otx2_nic *priv, u16 pcifunc) { int rep_id; for (rep_id = 0; rep_id < priv->rep_cnt; rep_id++) if (priv->rep_pf_map[rep_id] == pcifunc) return rep_id; return -EINVAL; } static int rvu_rep_notify_pfvf(struct otx2_nic *priv, u16 event, struct rep_event *data) { struct rep_event *req; mutex_lock(&priv->mbox.lock); req = otx2_mbox_alloc_msg_rep_event_notify(&priv->mbox); if (!req) { mutex_unlock(&priv->mbox.lock); return -ENOMEM; } req->event = event; req->pcifunc = data->pcifunc; memcpy(&req->evt_data, &data->evt_data, sizeof(struct rep_evt_data)); otx2_sync_mbox_msg(&priv->mbox); mutex_unlock(&priv->mbox.lock); return 0; } static void rvu_rep_state_evt_handler(struct otx2_nic *priv, struct rep_event *info) { struct rep_dev *rep; int rep_id; rep_id = rvu_rep_get_repid(priv, info->pcifunc); rep = priv->reps[rep_id]; if (info->evt_data.vf_state) rep->flags |= RVU_REP_VF_INITIALIZED; else rep->flags &= ~RVU_REP_VF_INITIALIZED; } int rvu_event_up_notify(struct otx2_nic *pf, struct rep_event *info) { if (info->event & RVU_EVENT_PFVF_STATE) rvu_rep_state_evt_handler(pf, info); return 0; } static int rvu_rep_change_mtu(struct net_device *dev, int new_mtu) { struct rep_dev *rep = netdev_priv(dev); struct otx2_nic *priv = rep->mdev; struct rep_event evt = {0}; netdev_info(dev, "Changing MTU from %d to %d\n", dev->mtu, new_mtu); dev->mtu = new_mtu; evt.evt_data.mtu = new_mtu; evt.pcifunc = rep->pcifunc; rvu_rep_notify_pfvf(priv, RVU_EVENT_MTU_CHANGE, &evt); return 0; } static void rvu_rep_get_stats(struct work_struct *work) { struct delayed_work *del_work = to_delayed_work(work); struct nix_stats_req *req; struct nix_stats_rsp *rsp; struct rep_stats *stats; struct otx2_nic *priv; struct rep_dev *rep; int err; rep = container_of(del_work, struct rep_dev, stats_wrk); priv = rep->mdev; mutex_lock(&priv->mbox.lock); req = otx2_mbox_alloc_msg_nix_lf_stats(&priv->mbox); if (!req) { mutex_unlock(&priv->mbox.lock); return; } req->pcifunc = rep->pcifunc; err = otx2_sync_mbox_msg_busy_poll(&priv->mbox); if (err) goto exit; rsp = (struct nix_stats_rsp *) otx2_mbox_get_rsp(&priv->mbox.mbox, 0, &req->hdr); if (IS_ERR(rsp)) { err = PTR_ERR(rsp); goto exit; } stats = &rep->stats; stats->rx_bytes = rsp->rx.octs; stats->rx_frames = rsp->rx.ucast + rsp->rx.bcast + rsp->rx.mcast; stats->rx_drops = rsp->rx.drop; stats->rx_mcast_frames = rsp->rx.mcast; stats->tx_bytes = rsp->tx.octs; stats->tx_frames = rsp->tx.ucast + rsp->tx.bcast + rsp->tx.mcast; stats->tx_drops = rsp->tx.drop; exit: mutex_unlock(&priv->mbox.lock); } static void rvu_rep_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { struct rep_dev *rep = netdev_priv(dev); if (!(rep->flags & RVU_REP_VF_INITIALIZED)) return; stats->rx_packets = rep->stats.rx_frames; stats->rx_bytes = rep->stats.rx_bytes; stats->rx_dropped = rep->stats.rx_drops; stats->multicast = rep->stats.rx_mcast_frames; stats->tx_packets = rep->stats.tx_frames; stats->tx_bytes = rep->stats.tx_bytes; stats->tx_dropped = rep->stats.tx_drops; schedule_delayed_work(&rep->stats_wrk, msecs_to_jiffies(100)); } static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena) { struct esw_cfg_req *req; mutex_lock(&priv->mbox.lock); req = otx2_mbox_alloc_msg_esw_cfg(&priv->mbox); if (!req) { mutex_unlock(&priv->mbox.lock); return -ENOMEM; } req->ena = ena; otx2_sync_mbox_msg(&priv->mbox); mutex_unlock(&priv->mbox.lock); return 0; } static netdev_tx_t rvu_rep_xmit(struct sk_buff *skb, struct net_device *dev) { struct rep_dev *rep = netdev_priv(dev); struct otx2_nic *pf = rep->mdev; struct otx2_snd_queue *sq; struct netdev_queue *txq; sq = &pf->qset.sq[rep->rep_id]; txq = netdev_get_tx_queue(dev, 0); if (!otx2_sq_append_skb(pf, txq, sq, skb, rep->rep_id)) { netif_tx_stop_queue(txq); /* Check again, in case SQBs got freed up */ smp_mb(); if (((sq->num_sqbs - *sq->aura_fc_addr) * sq->sqe_per_sqb) > sq->sqe_thresh) netif_tx_wake_queue(txq); return NETDEV_TX_BUSY; } return NETDEV_TX_OK; } static int rvu_rep_open(struct net_device *dev) { struct rep_dev *rep = netdev_priv(dev); struct otx2_nic *priv = rep->mdev; struct rep_event evt = {0}; if (!(rep->flags & RVU_REP_VF_INITIALIZED)) return 0; netif_carrier_on(dev); netif_tx_start_all_queues(dev); evt.event = RVU_EVENT_PORT_STATE; evt.evt_data.port_state = 1; evt.pcifunc = rep->pcifunc; rvu_rep_notify_pfvf(priv, RVU_EVENT_PORT_STATE, &evt); return 0; } static int rvu_rep_stop(struct net_device *dev) { struct rep_dev *rep = netdev_priv(dev); struct otx2_nic *priv = rep->mdev; struct rep_event evt = {0}; if (!(rep->flags & RVU_REP_VF_INITIALIZED)) return 0; netif_carrier_off(dev); netif_tx_disable(dev); evt.event = RVU_EVENT_PORT_STATE; evt.pcifunc = rep->pcifunc; rvu_rep_notify_pfvf(priv, RVU_EVENT_PORT_STATE, &evt); return 0; } static const struct net_device_ops rvu_rep_netdev_ops = { .ndo_open = rvu_rep_open, .ndo_stop = rvu_rep_stop, .ndo_start_xmit = rvu_rep_xmit, .ndo_get_stats64 = rvu_rep_get_stats64, .ndo_change_mtu = rvu_rep_change_mtu, .ndo_has_offload_stats = rvu_rep_has_offload_stats, .ndo_get_offload_stats = rvu_rep_get_offload_stats, .ndo_setup_tc = rvu_rep_setup_tc, }; static int rvu_rep_napi_init(struct otx2_nic *priv, struct netlink_ext_ack *extack) { struct otx2_qset *qset = &priv->qset; struct otx2_cq_poll *cq_poll = NULL; struct otx2_hw *hw = &priv->hw; int err = 0, qidx, vec; char *irq_name; qset->napi = kcalloc(hw->cint_cnt, sizeof(*cq_poll), GFP_KERNEL); if (!qset->napi) return -ENOMEM; /* Register NAPI handler */ for (qidx = 0; qidx < hw->cint_cnt; qidx++) { cq_poll = &qset->napi[qidx]; cq_poll->cint_idx = qidx; cq_poll->cq_ids[CQ_RX] = (qidx < hw->rx_queues) ? qidx : CINT_INVALID_CQ; cq_poll->cq_ids[CQ_TX] = (qidx < hw->tx_queues) ? qidx + hw->rx_queues : CINT_INVALID_CQ; cq_poll->cq_ids[CQ_XDP] = CINT_INVALID_CQ; cq_poll->cq_ids[CQ_QOS] = CINT_INVALID_CQ; cq_poll->dev = (void *)priv; netif_napi_add(priv->reps[qidx]->netdev, &cq_poll->napi, otx2_napi_handler); napi_enable(&cq_poll->napi); } /* Register CQ IRQ handlers */ vec = hw->nix_msixoff + NIX_LF_CINT_VEC_START; for (qidx = 0; qidx < hw->cint_cnt; qidx++) { irq_name = &hw->irq_name[vec * NAME_SIZE]; snprintf(irq_name, NAME_SIZE, "rep%d-rxtx-%d", qidx, qidx); err = request_irq(pci_irq_vector(priv->pdev, vec), otx2_cq_intr_handler, 0, irq_name, &qset->napi[qidx]); if (err) { NL_SET_ERR_MSG_FMT_MOD(extack, "RVU REP IRQ registration failed for CQ%d", qidx); goto err_free_cints; } vec++; /* Enable CQ IRQ */ otx2_write64(priv, NIX_LF_CINTX_INT(qidx), BIT_ULL(0)); otx2_write64(priv, NIX_LF_CINTX_ENA_W1S(qidx), BIT_ULL(0)); } priv->flags &= ~OTX2_FLAG_INTF_DOWN; return 0; err_free_cints: otx2_free_cints(priv, qidx); otx2_disable_napi(priv); return err; } static void rvu_rep_free_cq_rsrc(struct otx2_nic *priv) { struct otx2_qset *qset = &priv->qset; struct otx2_cq_poll *cq_poll = NULL; int qidx, vec; /* Cleanup CQ NAPI and IRQ */ vec = priv->hw.nix_msixoff + NIX_LF_CINT_VEC_START; for (qidx = 0; qidx < priv->hw.cint_cnt; qidx++) { /* Disable interrupt */ otx2_write64(priv, NIX_LF_CINTX_ENA_W1C(qidx), BIT_ULL(0)); synchronize_irq(pci_irq_vector(priv->pdev, vec)); cq_poll = &qset->napi[qidx]; napi_synchronize(&cq_poll->napi); vec++; } otx2_free_cints(priv, priv->hw.cint_cnt); otx2_disable_napi(priv); } static void rvu_rep_rsrc_free(struct otx2_nic *priv) { struct otx2_qset *qset = &priv->qset; struct delayed_work *work; int wrk; for (wrk = 0; wrk < priv->qset.cq_cnt; wrk++) { work = &priv->refill_wrk[wrk].pool_refill_work; cancel_delayed_work_sync(work); } devm_kfree(priv->dev, priv->refill_wrk); otx2_free_hw_resources(priv); otx2_free_queue_mem(qset); } static int rvu_rep_rsrc_init(struct otx2_nic *priv) { struct otx2_qset *qset = &priv->qset; int err; err = otx2_alloc_queue_mem(priv); if (err) return err; priv->hw.max_mtu = otx2_get_max_mtu(priv); priv->tx_max_pktlen = priv->hw.max_mtu + OTX2_ETH_HLEN; priv->rbsize = ALIGN(priv->hw.rbuf_len, OTX2_ALIGN) + OTX2_HEAD_ROOM; err = otx2_init_hw_resources(priv); if (err) goto err_free_rsrc; /* Set maximum frame size allowed in HW */ err = otx2_hw_set_mtu(priv, priv->hw.max_mtu); if (err) { dev_err(priv->dev, "Failed to set HW MTU\n"); goto err_free_rsrc; } return 0; err_free_rsrc: otx2_free_hw_resources(priv); otx2_free_queue_mem(qset); return err; } void rvu_rep_destroy(struct otx2_nic *priv) { struct rep_dev *rep; int rep_id; rvu_eswitch_config(priv, false); priv->flags |= OTX2_FLAG_INTF_DOWN; rvu_rep_free_cq_rsrc(priv); for (rep_id = 0; rep_id < priv->rep_cnt; rep_id++) { rep = priv->reps[rep_id]; unregister_netdev(rep->netdev); rvu_rep_devlink_port_unregister(rep); free_netdev(rep->netdev); kfree(rep->flow_cfg); } kfree(priv->reps); rvu_rep_rsrc_free(priv); } int rvu_rep_create(struct otx2_nic *priv, struct netlink_ext_ack *extack) { int rep_cnt = priv->rep_cnt; struct net_device *ndev; struct rep_dev *rep; int rep_id, err; u16 pcifunc; err = rvu_rep_rsrc_init(priv); if (err) return -ENOMEM; priv->reps = kcalloc(rep_cnt, sizeof(struct rep_dev *), GFP_KERNEL); if (!priv->reps) return -ENOMEM; for (rep_id = 0; rep_id < rep_cnt; rep_id++) { ndev = alloc_etherdev(sizeof(*rep)); if (!ndev) { NL_SET_ERR_MSG_FMT_MOD(extack, "PFVF representor:%d creation failed", rep_id); err = -ENOMEM; goto exit; } rep = netdev_priv(ndev); priv->reps[rep_id] = rep; rep->mdev = priv; rep->netdev = ndev; rep->rep_id = rep_id; ndev->min_mtu = OTX2_MIN_MTU; ndev->max_mtu = priv->hw.max_mtu; ndev->netdev_ops = &rvu_rep_netdev_ops; pcifunc = priv->rep_pf_map[rep_id]; rep->pcifunc = pcifunc; snprintf(ndev->name, sizeof(ndev->name), "Rpf%dvf%d", rvu_get_pf(pcifunc), (pcifunc & RVU_PFVF_FUNC_MASK)); ndev->hw_features = (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXHASH | NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6); ndev->hw_features |= NETIF_F_HW_TC; ndev->features |= ndev->hw_features; eth_hw_addr_random(ndev); err = rvu_rep_devlink_port_register(rep); if (err) goto exit; SET_NETDEV_DEVLINK_PORT(ndev, &rep->dl_port); err = register_netdev(ndev); if (err) { NL_SET_ERR_MSG_MOD(extack, "PFVF representor registration failed"); free_netdev(ndev); goto exit; } INIT_DELAYED_WORK(&rep->stats_wrk, rvu_rep_get_stats); } err = rvu_rep_napi_init(priv, extack); if (err) goto exit; rvu_eswitch_config(priv, true); return 0; exit: while (--rep_id >= 0) { rep = priv->reps[rep_id]; unregister_netdev(rep->netdev); rvu_rep_devlink_port_unregister(rep); free_netdev(rep->netdev); } kfree(priv->reps); rvu_rep_rsrc_free(priv); return err; } static int rvu_get_rep_cnt(struct otx2_nic *priv) { struct get_rep_cnt_rsp *rsp; struct mbox_msghdr *msghdr; struct msg_req *req; int err, rep; mutex_lock(&priv->mbox.lock); req = otx2_mbox_alloc_msg_get_rep_cnt(&priv->mbox); if (!req) { mutex_unlock(&priv->mbox.lock); return -ENOMEM; } err = otx2_sync_mbox_msg(&priv->mbox); if (err) goto exit; msghdr = otx2_mbox_get_rsp(&priv->mbox.mbox, 0, &req->hdr); if (IS_ERR(msghdr)) { err = PTR_ERR(msghdr); goto exit; } rsp = (struct get_rep_cnt_rsp *)msghdr; priv->hw.tx_queues = rsp->rep_cnt; priv->hw.rx_queues = rsp->rep_cnt; priv->rep_cnt = rsp->rep_cnt; for (rep = 0; rep < priv->rep_cnt; rep++) priv->rep_pf_map[rep] = rsp->rep_pf_map[rep]; exit: mutex_unlock(&priv->mbox.lock); return err; } static int rvu_rep_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct device *dev = &pdev->dev; struct otx2_nic *priv; struct otx2_hw *hw; int err; err = pcim_enable_device(pdev); if (err) { dev_err(dev, "Failed to enable PCI device\n"); return err; } err = pci_request_regions(pdev, DRV_NAME); if (err) { dev_err(dev, "PCI request regions failed 0x%x\n", err); return err; } err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); if (err) { dev_err(dev, "DMA mask config failed, abort\n"); goto err_release_regions; } pci_set_master(pdev); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) { err = -ENOMEM; goto err_release_regions; } pci_set_drvdata(pdev, priv); priv->pdev = pdev; priv->dev = dev; priv->flags |= OTX2_FLAG_INTF_DOWN; priv->flags |= OTX2_FLAG_REP_MODE_ENABLED; hw = &priv->hw; hw->pdev = pdev; hw->max_queues = OTX2_MAX_CQ_CNT; hw->rbuf_len = OTX2_DEFAULT_RBUF_LEN; hw->xqe_size = 128; err = otx2_init_rsrc(pdev, priv); if (err) goto err_release_regions; priv->iommu_domain = iommu_get_domain_for_dev(dev); err = rvu_get_rep_cnt(priv); if (err) goto err_detach_rsrc; err = otx2_register_dl(priv); if (err) goto err_detach_rsrc; return 0; err_detach_rsrc: if (priv->hw.lmt_info) free_percpu(priv->hw.lmt_info); if (test_bit(CN10K_LMTST, &priv->hw.cap_flag)) qmem_free(priv->dev, priv->dync_lmt); otx2_detach_resources(&priv->mbox); otx2_disable_mbox_intr(priv); otx2_pfaf_mbox_destroy(priv); pci_free_irq_vectors(pdev); err_release_regions: pci_set_drvdata(pdev, NULL); pci_release_regions(pdev); return err; } static void rvu_rep_remove(struct pci_dev *pdev) { struct otx2_nic *priv = pci_get_drvdata(pdev); otx2_unregister_dl(priv); if (!(priv->flags & OTX2_FLAG_INTF_DOWN)) rvu_rep_destroy(priv); otx2_detach_resources(&priv->mbox); if (priv->hw.lmt_info) free_percpu(priv->hw.lmt_info); if (test_bit(CN10K_LMTST, &priv->hw.cap_flag)) qmem_free(priv->dev, priv->dync_lmt); otx2_disable_mbox_intr(priv); otx2_pfaf_mbox_destroy(priv); pci_free_irq_vectors(priv->pdev); pci_set_drvdata(pdev, NULL); pci_release_regions(pdev); } static struct pci_driver rvu_rep_driver = { .name = DRV_NAME, .id_table = rvu_rep_id_table, .probe = rvu_rep_probe, .remove = rvu_rep_remove, .shutdown = rvu_rep_remove, }; static int __init rvu_rep_init_module(void) { return pci_register_driver(&rvu_rep_driver); } static void __exit rvu_rep_cleanup_module(void) { pci_unregister_driver(&rvu_rep_driver); } module_init(rvu_rep_init_module); module_exit(rvu_rep_cleanup_module);