// SPDX-License-Identifier: GPL-2.0-only
/*
* Huawei HiNIC PCI Express Linux driver
* Copyright(c) 2017 Huawei Technologies Co., Ltd
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/log2.h>
#include <linux/err.h>
#include <linux/netdevice.h>
#include "hinic_sriov.h"
#include "hinic_hw_if.h"
#include "hinic_hw_eqs.h"
#include "hinic_hw_mgmt.h"
#include "hinic_hw_qp_ctxt.h"
#include "hinic_hw_qp.h"
#include "hinic_hw_io.h"
#include "hinic_hw_dev.h"
#define IO_STATUS_TIMEOUT 100
#define OUTBOUND_STATE_TIMEOUT 100
#define DB_STATE_TIMEOUT 100
#define MAX_IRQS(max_qps, num_aeqs, num_ceqs) \
(2 * (max_qps) + (num_aeqs) + (num_ceqs))
#define ADDR_IN_4BYTES(addr) ((addr) >> 2)
enum intr_type {
INTR_MSIX_TYPE,
};
enum io_status {
IO_STOPPED = 0,
IO_RUNNING = 1,
};
/**
* get_capability - convert device capabilities to NIC capabilities
* @hwdev: the HW device to set and convert device capabilities for
* @dev_cap: device capabilities from FW
*
* Return 0 - Success, negative - Failure
**/
static int parse_capability(struct hinic_hwdev *hwdev,
struct hinic_dev_cap *dev_cap)
{
struct hinic_cap *nic_cap = &hwdev->nic_cap;
int num_aeqs, num_ceqs, num_irqs;
if (!HINIC_IS_VF(hwdev->hwif) && dev_cap->intr_type != INTR_MSIX_TYPE)
return -EFAULT;
num_aeqs = HINIC_HWIF_NUM_AEQS(hwdev->hwif);
num_ceqs = HINIC_HWIF_NUM_CEQS(hwdev->hwif);
num_irqs = HINIC_HWIF_NUM_IRQS(hwdev->hwif);
/* Each QP has its own (SQ + RQ) interrupts */
nic_cap->num_qps = (num_irqs - (num_aeqs + num_ceqs)) / 2;
if (nic_cap->num_qps > HINIC_Q_CTXT_MAX)
nic_cap->num_qps = HINIC_Q_CTXT_MAX;
if (!HINIC_IS_VF(hwdev->hwif))
nic_cap->max_qps = dev_cap->max_sqs + 1;
else
nic_cap->max_qps = dev_cap->max_sqs;
if (nic_cap->num_qps > nic_cap->max_qps)
nic_cap->num_qps = nic_cap->max_qps;
if (!HINIC_IS_VF(hwdev->hwif)) {
nic_cap->max_vf = dev_cap->max_vf;
nic_cap->max_vf_qps = dev_cap->max_vf_sqs + 1;
}
hwdev->port_id = dev_cap->port_id;
return 0;
}
/**
* get_cap_from_fw - get device capabilities from FW
* @pfhwdev: the PF HW device to get capabilities for
*
* Return 0 - Success, negative - Failure
**/
static int get_capability(struct hinic_pfhwdev *pfhwdev)
{
struct hinic_hwdev *hwdev = &pfhwdev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
struct hinic_dev_cap dev_cap;
u16 out_len;
int err;
out_len = sizeof(dev_cap);
err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_CFGM,
HINIC_CFG_NIC_CAP, &dev_cap, sizeof(dev_cap),
&dev_cap, &out_len, HINIC_MGMT_MSG_SYNC);
if (err) {
dev_err(&pdev->dev, "Failed to get capability from FW\n");
return err;
}
return parse_capability(hwdev, &dev_cap);
}
/**
* get_dev_cap - get device capabilities
* @hwdev: the NIC HW device to get capabilities for
*
* Return 0 - Success, negative - Failure
**/
static int get_dev_cap(struct hinic_hwdev *hwdev)
{
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
struct hinic_pfhwdev *pfhwdev;
int err;
switch (HINIC_FUNC_TYPE(hwif)) {
case HINIC_PPF:
case HINIC_PF:
case HINIC_VF:
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
err = get_capability(pfhwdev);
if (err) {
dev_err(&pdev->dev, "Failed to get capability\n");
return err;
}
break;
default:
dev_err(&pdev->dev, "Unsupported PCI Function type\n");
return -EINVAL;
}