#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <linux/bitops.h>
#include <linux/irq.h>
#include <asm/byteorder.h>
#include <linux/bitmap.h>
#include <linux/auxiliary_bus.h>
#include <linux/bnge/hsi.h>
#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 );
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;
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_obj(*aux_priv);
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_obj(*auxr_dev);
if (!auxr_dev)
goto aux_dev_uninit;
aux_priv->auxr_dev = auxr_dev;
auxr_info = kzalloc_obj(*auxr_info);
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;
}