// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2025 Broadcom. #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bnge.h" #include "bnge_hwrm.h" #include "bnge_auxr.h" static DEFINE_IDA(bnge_aux_dev_ids); static void bnge_fill_msix_vecs(struct bnge_dev *bd, struct bnge_msix_info *info) { struct bnge_auxr_dev *auxr_dev = bd->auxr_dev; int num_msix, i; if (!auxr_dev->auxr_info->msix_requested) { dev_warn(bd->dev, "Requested MSI-X vectors not allocated\n"); return; } num_msix = auxr_dev->auxr_info->msix_requested; for (i = 0; i < num_msix; i++) { info[i].vector = bd->irq_tbl[i].vector; info[i].db_offset = bd->db_offset; info[i].ring_idx = i; } } int bnge_register_dev(struct bnge_auxr_dev *auxr_dev, void *handle) { struct bnge_dev *bd = pci_get_drvdata(auxr_dev->pdev); struct bnge_auxr_info *auxr_info; int rc = 0; netdev_lock(bd->netdev); mutex_lock(&auxr_dev->auxr_dev_lock); if (!bd->irq_tbl) { rc = -ENODEV; goto exit; } if (!bnge_aux_has_enough_resources(bd)) { rc = -ENOMEM; goto exit; } auxr_info = auxr_dev->auxr_info; auxr_info->handle = handle; auxr_info->msix_requested = bd->aux_num_msix; bnge_fill_msix_vecs(bd, bd->auxr_dev->msix_info); auxr_dev->flags |= BNGE_ARDEV_MSIX_ALLOC; exit: mutex_unlock(&auxr_dev->auxr_dev_lock); netdev_unlock(bd->netdev); return rc; } EXPORT_SYMBOL(bnge_register_dev); void bnge_unregister_dev(struct bnge_auxr_dev *auxr_dev) { struct bnge_dev *bd = pci_get_drvdata(auxr_dev->pdev); struct bnge_auxr_info *auxr_info; auxr_info = auxr_dev->auxr_info; netdev_lock(bd->netdev); mutex_lock(&auxr_dev->auxr_dev_lock); if (auxr_info->msix_requested) auxr_dev->flags &= ~BNGE_ARDEV_MSIX_ALLOC; auxr_info->msix_requested = 0; mutex_unlock(&auxr_dev->auxr_dev_lock); netdev_unlock(bd->netdev); } EXPORT_SYMBOL(bnge_unregister_dev); int bnge_send_msg(struct bnge_auxr_dev *auxr_dev, struct bnge_fw_msg *fw_msg) { struct bnge_dev *bd = pci_get_drvdata(auxr_dev->pdev); struct output *resp; struct input *req; u32 resp_len; int rc; rc = bnge_hwrm_req_init(bd, req, 0 /* don't care */); if (rc) return rc; rc = bnge_hwrm_req_replace(bd, req, fw_msg->msg, fw_msg->msg_len); if (rc) goto drop_req; bnge_hwrm_req_timeout(bd, req, fw_msg->timeout); resp = bnge_hwrm_req_hold(bd, req); rc = bnge_hwrm_req_send(bd, req); resp_len = le16_to_cpu(resp->resp_len); if (resp_len) { if (fw_msg->resp_max_len < resp_len) resp_len = fw_msg->resp_max_len; memcpy(fw_msg->resp, resp, resp_len); } drop_req: bnge_hwrm_req_drop(bd, req); return rc; } EXPORT_SYMBOL(bnge_send_msg); void bnge_rdma_aux_device_uninit(struct bnge_dev *bd) { struct bnge_auxr_priv *aux_priv; struct auxiliary_device *adev; /* Skip if no auxiliary device init was done. */ if (!bd->aux_priv) return; aux_priv = bd->aux_priv; adev = &aux_priv->aux_dev; auxiliary_device_uninit(adev); } static void bnge_aux_dev_release(struct device *dev) { struct bnge_auxr_priv *aux_priv = container_of(dev, struct bnge_auxr_priv, aux_dev.dev); struct bnge_dev *bd = pci_get_drvdata(aux_priv->auxr_dev->pdev); ida_free(&bnge_aux_dev_ids, aux_priv->id); kfree(aux_priv->auxr_dev->auxr_info); bd->auxr_dev = NULL; kfree(aux_priv->auxr_dev); kfree(aux_priv); bd->aux_priv = NULL; } void bnge_rdma_aux_device_del(struct bnge_dev *bd) { if (!bd->auxr_dev) return; auxiliary_device_delete(&bd->aux_priv->aux_dev); } static void bnge_set_auxr_dev_info(struct bnge_auxr_dev *auxr_dev, struct bnge_dev *bd) { auxr_dev->pdev = bd->pdev; auxr_dev->l2_db_size = bd->db_size; auxr_dev->l2_db_size_nc = bd->db_size; auxr_dev->l2_db_offset = bd->db_offset; mutex_init(&auxr_dev->auxr_dev_lock); if (bd->flags & BNGE_EN_ROCE_V1) auxr_dev->flags |= BNGE_ARDEV_ROCEV1_SUPP; if (bd->flags & BNGE_EN_ROCE_V2) auxr_dev->flags |= BNGE_ARDEV_ROCEV2_SUPP; auxr_dev->chip_num = bd->chip_num; auxr_dev->hw_ring_stats_size = bd->hw_ring_stats_size; auxr_dev->pf_port_id = bd->pf.port_id; auxr_dev->en_state = bd->state; auxr_dev->bar0 = bd->bar0; } void bnge_rdma_aux_device_add(struct bnge_dev *bd) { struct auxiliary_device *aux_dev; int rc; if (!bd->auxr_dev) return; aux_dev = &bd->aux_priv->aux_dev; rc = auxiliary_device_add(aux_dev); if (rc) { dev_warn(bd->dev, "Failed to add auxiliary device for ROCE\n"); auxiliary_device_uninit(aux_dev); bd->flags &= ~BNGE_EN_ROCE; } bd->auxr_dev->net = bd->netdev; } void bnge_rdma_aux_device_init(struct bnge_dev *bd) { struct auxiliary_device *aux_dev; struct bnge_auxr_info *auxr_info; struct bnge_auxr_priv *aux_priv; struct bnge_auxr_dev *auxr_dev; int rc; if (!bnge_is_roce_en(bd)) return; aux_priv = kzalloc(sizeof(*aux_priv), GFP_KERNEL); if (!aux_priv) goto exit; aux_priv->id = ida_alloc(&bnge_aux_dev_ids, GFP_KERNEL); if (aux_priv->id < 0) { dev_warn(bd->dev, "ida alloc failed for aux device\n"); kfree(aux_priv); goto exit; } aux_dev = &aux_priv->aux_dev; aux_dev->id = aux_priv->id; aux_dev->name = "rdma"; aux_dev->dev.parent = &bd->pdev->dev; aux_dev->dev.release = bnge_aux_dev_release; rc = auxiliary_device_init(aux_dev); if (rc) { ida_free(&bnge_aux_dev_ids, aux_priv->id); kfree(aux_priv); goto exit; } bd->aux_priv = aux_priv; auxr_dev = kzalloc(sizeof(*auxr_dev), GFP_KERNEL); if (!auxr_dev) goto aux_dev_uninit; aux_priv->auxr_dev = auxr_dev; auxr_info = kzalloc(sizeof(*auxr_info), GFP_KERNEL); if (!auxr_info) goto aux_dev_uninit; auxr_dev->auxr_info = auxr_info; bd->auxr_dev = auxr_dev; bnge_set_auxr_dev_info(auxr_dev, bd); return; aux_dev_uninit: auxiliary_device_uninit(aux_dev); exit: bd->flags &= ~BNGE_EN_ROCE; }