#define _HINIC3_CSR_H_
#define HINIC3_CFG_REGS_FLAG 0x40000000
+#define HINIC3_MGMT_REGS_FLAG 0xC0000000
#define HINIC3_REGS_FLAG_MASK 0x3FFFFFFF
#define HINIC3_VF_CFG_REG_OFFSET 0x2000
#define HINIC3_FUNC_CSR_MAILBOX_RESULT_H_OFF (HINIC3_CFG_REGS_FLAG + 0x0108)
#define HINIC3_FUNC_CSR_MAILBOX_RESULT_L_OFF (HINIC3_CFG_REGS_FLAG + 0x010C)
+#define HINIC3_HOST_CSR_BASE_ADDR (HINIC3_MGMT_REGS_FLAG + 0x6000)
+#define HINIC3_PPF_ELECTION_OFFSET 0x0
+#define HINIC3_CSR_PPF_ELECTION_ADDR \
+ (HINIC3_HOST_CSR_BASE_ADDR + HINIC3_PPF_ELECTION_OFFSET)
+
#define HINIC3_CSR_DMA_ATTR_TBL_ADDR (HINIC3_CFG_REGS_FLAG + 0x380)
#define HINIC3_CSR_DMA_ATTR_INDIR_IDX_ADDR (HINIC3_CFG_REGS_FLAG + 0x390)
ret = -EFAULT;
}
+ hinic3_set_pf_status(hwif, HINIC3_PF_STATUS_FLR_START_FLAG);
+
clr_res.func_id = hwif->attr.func_global_idx;
msg_params.buf_in = &clr_res;
msg_params.in_size = sizeof(clr_res);
return ret;
}
+int hinic3_set_bdf_ctxt(struct hinic3_hwdev *hwdev,
+ struct comm_cmd_bdf_info *bdf_info)
+{
+ struct mgmt_msg_params msg_params = {};
+ int err;
+
+ mgmt_msg_params_init_default(&msg_params, bdf_info, sizeof(*bdf_info));
+
+ err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
+ COMM_CMD_SEND_BDF_INFO, &msg_params);
+ if (err || bdf_info->head.status) {
+ dev_err(hwdev->dev,
+ "Failed to set bdf info to fw, err: %d, status: 0x%x\n",
+ err, bdf_info->head.status);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int hinic3_sync_time(struct hinic3_hwdev *hwdev, u64 time)
+{
+ struct comm_cmd_sync_time time_info = {};
+ struct mgmt_msg_params msg_params = {};
+ int err;
+
+ time_info.mstime = time;
+
+ mgmt_msg_params_init_default(&msg_params, &time_info,
+ sizeof(time_info));
+
+ err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
+ COMM_CMD_SYNC_TIME, &msg_params);
+ if (err || time_info.head.status) {
+ dev_err(hwdev->dev,
+ "Failed to sync time to mgmt, err: %d, status: 0x%x\n",
+ err, time_info.head.status);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+void hinic3_sync_time_to_fw(struct hinic3_hwdev *hwdev)
+{
+ struct timespec64 ts = {};
+ u64 time;
+ int err;
+
+ ktime_get_real_ts64(&ts);
+ time = (u64)(ts.tv_sec * MSEC_PER_SEC + ts.tv_nsec / NSEC_PER_MSEC);
+
+ err = hinic3_sync_time(hwdev, time);
+ if (err)
+ dev_err(hwdev->dev,
+ "Synchronize UTC time to firmware failed, err=%d\n",
+ err);
+}
+
static int get_hw_rx_buf_size_idx(int rx_buf_sz, u16 *buf_sz_idx)
{
/* Supported RX buffer sizes in bytes. Configured by array index. */
u32 page_size);
int hinic3_set_cmdq_depth(struct hinic3_hwdev *hwdev, u16 cmdq_depth);
int hinic3_func_rx_tx_flush(struct hinic3_hwdev *hwdev);
+int hinic3_set_bdf_ctxt(struct hinic3_hwdev *hwdev,
+ struct comm_cmd_bdf_info *bdf_info);
+void hinic3_sync_time_to_fw(struct hinic3_hwdev *hwdev);
+
int hinic3_set_root_ctxt(struct hinic3_hwdev *hwdev, u32 rq_depth, u32 sq_depth,
int rx_buf_sz);
int hinic3_clean_root_ctxt(struct hinic3_hwdev *hwdev);
COMM_CMD_CFG_MSIX_CTRL_REG = 23,
COMM_CMD_SET_CEQ_CTRL_REG = 24,
COMM_CMD_SET_DMA_ATTR = 25,
+
+ /* Commands for obtaining information */
+ COMM_CMD_SYNC_TIME = 62,
+ COMM_CMD_SEND_BDF_INFO = 64,
};
struct comm_cmd_cfg_msix_ctrl_reg {
u16 rsvd1[3];
};
+struct comm_cmd_sync_time {
+ struct mgmt_msg_head head;
+
+ u64 mstime;
+ u64 rsvd1;
+};
+
+struct comm_cmd_bdf_info {
+ struct mgmt_msg_head head;
+
+ u16 function_idx;
+ u8 rsvd1[2];
+ u8 bus;
+ u8 device;
+ u8 function;
+ u8 rsvd2[5];
+};
+
/* Services supported by HW. HW uses these values when delivering events.
* HW supports multiple services that are not yet supported by driver
* (e.g. RoCE).
#define HINIC3_PCIE_SNOOP 0
#define HINIC3_PCIE_TPH_DISABLE 0
+#define HINIC3_SYNFW_TIME_PERIOD (60 * 60 * 1000)
+
#define HINIC3_DMA_ATTR_INDIR_IDX_MASK GENMASK(9, 0)
#define HINIC3_DMA_ATTR_INDIR_IDX_SET(val, member) \
FIELD_PREP(HINIC3_DMA_ATTR_INDIR_##member##_MASK, val)
#define HINIC3_WQ_MAX_REQ 10
enum hinic3_hwdev_init_state {
+ HINIC3_HWDEV_MGMT_INITED = 1,
HINIC3_HWDEV_MBOX_INITED = 2,
HINIC3_HWDEV_CMDQ_INITED = 3,
};
goto err_clear_func_svc_used_state;
}
+ hinic3_set_pf_status(hwdev->hwif, HINIC3_PF_STATUS_ACTIVE_FLAG);
+
return 0;
err_clear_func_svc_used_state:
static void hinic3_uninit_comm_ch(struct hinic3_hwdev *hwdev)
{
+ hinic3_set_pf_status(hwdev->hwif, HINIC3_PF_STATUS_INIT);
hinic3_free_cmdqs_channel(hwdev);
hinic3_set_func_svc_used_state(hwdev, COMM_FUNC_SVC_T_COMM, 0);
free_base_mgmt_channel(hwdev);
}
+static void hinic3_auto_sync_time_work(struct work_struct *work)
+{
+ struct delayed_work *delay = to_delayed_work(work);
+ struct hinic3_hwdev *hwdev;
+
+ hwdev = container_of(delay, struct hinic3_hwdev, sync_time_task);
+
+ hinic3_sync_time_to_fw(hwdev);
+
+ queue_delayed_work(hwdev->workq, &hwdev->sync_time_task,
+ msecs_to_jiffies(HINIC3_SYNFW_TIME_PERIOD));
+}
+
+static void hinic3_init_ppf_work(struct hinic3_hwdev *hwdev)
+{
+ if (hinic3_ppf_idx(hwdev) != hinic3_global_func_id(hwdev))
+ return;
+
+ INIT_DELAYED_WORK(&hwdev->sync_time_task, hinic3_auto_sync_time_work);
+ queue_delayed_work(hwdev->workq, &hwdev->sync_time_task,
+ msecs_to_jiffies(HINIC3_SYNFW_TIME_PERIOD));
+}
+
+static void hinic3_free_ppf_work(struct hinic3_hwdev *hwdev)
+{
+ if (hinic3_ppf_idx(hwdev) != hinic3_global_func_id(hwdev))
+ return;
+
+ disable_delayed_work_sync(&hwdev->sync_time_task);
+}
+
static DEFINE_IDA(hinic3_adev_ida);
static int hinic3_adev_idx_alloc(void)
goto err_uninit_comm_ch;
}
+ hinic3_init_ppf_work(hwdev);
+
err = hinic3_set_comm_features(hwdev, hwdev->features,
COMM_MAX_FEATURE_QWORD);
if (err) {
dev_err(hwdev->dev, "Failed to set comm features\n");
- goto err_uninit_comm_ch;
+ goto err_free_ppf_work;
}
return 0;
+err_free_ppf_work:
+ hinic3_free_ppf_work(hwdev);
err_uninit_comm_ch:
hinic3_uninit_comm_ch(hwdev);
err_free_cfg_mgmt:
u64 drv_features[COMM_MAX_FEATURE_QWORD] = {};
hinic3_set_comm_features(hwdev, drv_features, COMM_MAX_FEATURE_QWORD);
+ hinic3_free_ppf_work(hwdev);
hinic3_func_rx_tx_flush(hwdev);
hinic3_uninit_comm_ch(hwdev);
hinic3_free_cfg_mgmt(hwdev);
void __iomem *cfg_reg_base;
void __iomem *intr_reg_base;
+ void __iomem *mgmt_reg_base;
void __iomem *db_base;
u64 db_dwqe_len;
u64 db_base_phy;
struct hinic3_ceqs *ceqs;
struct hinic3_mbox *mbox;
struct hinic3_cmdqs *cmdqs;
+ struct delayed_work sync_time_task;
struct workqueue_struct *workq;
/* protect channel init and uninit */
spinlock_t channel_lock;
#define HINIC3_AF0_GET(val, member) \
FIELD_GET(HINIC3_AF0_##member##_MASK, val)
+#define HINIC3_AF1_PPF_IDX_MASK GENMASK(5, 0)
#define HINIC3_AF1_AEQS_PER_FUNC_MASK GENMASK(9, 8)
#define HINIC3_AF1_MGMT_INIT_STATUS_MASK BIT(30)
#define HINIC3_AF1_GET(val, member) \
#define HINIC3_AF2_GET(val, member) \
FIELD_GET(HINIC3_AF2_##member##_MASK, val)
+#define HINIC3_AF3_GLOBAL_VF_ID_OF_PF_MASK GENMASK(27, 16)
+#define HINIC3_AF3_GET(val, member) \
+ FIELD_GET(HINIC3_AF3_##member##_MASK, val)
+
#define HINIC3_AF4_DOORBELL_CTRL_MASK BIT(0)
#define HINIC3_AF4_GET(val, member) \
FIELD_GET(HINIC3_AF4_##member##_MASK, val)
#define HINIC3_AF6_PF_STATUS_MASK GENMASK(15, 0)
#define HINIC3_AF6_FUNC_MAX_SQ_MASK GENMASK(31, 23)
#define HINIC3_AF6_MSIX_FLEX_EN_MASK BIT(22)
+#define HINIC3_AF6_SET(val, member) \
+ FIELD_PREP(HINIC3_AF6_##member##_MASK, val)
#define HINIC3_AF6_GET(val, member) \
FIELD_GET(HINIC3_AF6_##member##_MASK, val)
+#define HINIC3_PPF_ELECTION_IDX_MASK GENMASK(5, 0)
+#define HINIC3_PPF_ELECTION_SET(val, member) \
+ FIELD_PREP(HINIC3_PPF_ELECTION_##member##_MASK, val)
+#define HINIC3_PPF_ELECTION_GET(val, member) \
+ FIELD_GET(HINIC3_PPF_ELECTION_##member##_MASK, val)
+
#define HINIC3_GET_REG_ADDR(reg) ((reg) & (HINIC3_REGS_FLAG_MASK))
static void __iomem *hinic3_reg_addr(struct hinic3_hwif *hwif, u32 reg)
attr->pci_intf_idx = HINIC3_AF0_GET(attr0, PCI_INTF_IDX);
attr->func_type = HINIC3_AF0_GET(attr0, FUNC_TYPE);
+ attr->ppf_idx = HINIC3_AF1_GET(attr1, PPF_IDX);
attr->num_aeqs = BIT(HINIC3_AF1_GET(attr1, AEQS_PER_FUNC));
attr->num_ceqs = HINIC3_AF2_GET(attr2, CEQS_PER_FUNC);
attr->num_irqs = HINIC3_AF2_GET(attr2, IRQS_PER_FUNC);
if (attr->num_irqs > HINIC3_MAX_MSIX_ENTRY)
attr->num_irqs = HINIC3_MAX_MSIX_ENTRY;
+ attr->global_vf_id_of_pf = HINIC3_AF3_GET(attr3, GLOBAL_VF_ID_OF_PF);
+
attr->num_sq = HINIC3_AF6_GET(attr6, FUNC_MAX_SQ);
attr->msix_flex_en = HINIC3_AF6_GET(attr6, MSIX_FLEX_EN);
}
hinic3_hwif_write_reg(hwif, addr, attr4);
}
+static void hinic3_set_ppf(struct hinic3_hwdev *hwdev)
+{
+ struct hinic3_hwif *hwif = hwdev->hwif;
+ struct hinic3_func_attr *attr;
+ u32 addr, val;
+
+ if (HINIC3_IS_VF(hwdev))
+ return;
+
+ /* Read Modify Write */
+ attr = &hwif->attr;
+ addr = HINIC3_CSR_PPF_ELECTION_ADDR;
+ val = hinic3_hwif_read_reg(hwif, addr);
+ val &= ~HINIC3_PPF_ELECTION_IDX_MASK;
+ val |= HINIC3_PPF_ELECTION_SET(attr->func_global_idx, IDX);
+ hinic3_hwif_write_reg(hwif, addr, val);
+
+ /* Check PPF index */
+ val = hinic3_hwif_read_reg(hwif, addr);
+ attr->ppf_idx = HINIC3_PPF_ELECTION_GET(val, IDX);
+}
+
static int db_area_idx_init(struct hinic3_hwif *hwif, u64 db_base_phy,
u8 __iomem *db_base, u64 db_dwqe_len)
{
USEC_PER_MSEC);
}
+void hinic3_set_pf_status(struct hinic3_hwif *hwif,
+ enum hinic3_pf_status status)
+{
+ u32 attr6 = hinic3_hwif_read_reg(hwif, HINIC3_CSR_FUNC_ATTR6_ADDR);
+
+ attr6 &= ~HINIC3_AF6_PF_STATUS_MASK;
+ attr6 |= HINIC3_AF6_SET(status, PF_STATUS);
+
+ if (hwif->attr.func_type == HINIC3_FUNC_TYPE_VF)
+ return;
+
+ hinic3_hwif_write_reg(hwif, HINIC3_CSR_FUNC_ATTR6_ADDR, attr6);
+}
+
+enum hinic3_pf_status hinic3_get_pf_status(struct hinic3_hwif *hwif)
+{
+ u32 attr6 = hinic3_hwif_read_reg(hwif, HINIC3_CSR_FUNC_ATTR6_ADDR);
+
+ return HINIC3_AF6_GET(attr6, PF_STATUS);
+}
+
int hinic3_init_hwif(struct hinic3_hwdev *hwdev)
{
struct hinic3_pcidev *pci_adapter = hwdev->adapter;
return -ENOMEM;
hwdev->hwif = hwif;
- hwif->cfg_regs_base = (u8 __iomem *)pci_adapter->cfg_reg_base +
+ /* if function is VF, mgmt_regs_base will be NULL */
+ hwif->cfg_regs_base = pci_adapter->mgmt_reg_base ?
+ pci_adapter->cfg_reg_base :
+ (u8 __iomem *)pci_adapter->cfg_reg_base +
HINIC3_VF_CFG_REG_OFFSET;
+ hwif->intr_regs_base = pci_adapter->intr_reg_base;
+ hwif->mgmt_regs_base = pci_adapter->mgmt_reg_base;
+
err = db_area_idx_init(hwif, pci_adapter->db_base_phy,
pci_adapter->db_base,
pci_adapter->db_dwqe_len);
goto err_free_db_area_idx;
}
+ hinic3_set_ppf(hwdev);
+
disable_all_msix(hwdev);
+ /* disable mgmt cpu from reporting any event */
+ hinic3_set_pf_status(hwdev->hwif, HINIC3_PF_STATUS_INIT);
+
+ dev_dbg(hwdev->dev, "global_func_idx: %u, func_type: %d, host_id: %u, ppf: %u\n",
+ hwif->attr.func_global_idx, hwif->attr.func_type,
+ hwif->attr.pci_intf_idx, hwif->attr.ppf_idx);
return 0;
{
return hwdev->hwif->attr.func_global_idx;
}
+
+u8 hinic3_pf_id_of_vf(struct hinic3_hwdev *hwdev)
+{
+ return hwdev->hwif->attr.port_to_port_idx;
+}
+
+u16 hinic3_glb_pf_vf_offset(struct hinic3_hwdev *hwdev)
+{
+ return hwdev->hwif->attr.global_vf_id_of_pf;
+}
+
+u8 hinic3_ppf_idx(struct hinic3_hwdev *hwdev)
+{
+ return hwdev->hwif->attr.ppf_idx;
+}
struct hinic3_hwdev;
enum hinic3_func_type {
+ HINIC3_FUNC_TYPE_PF = 0,
HINIC3_FUNC_TYPE_VF = 1,
};
struct hinic3_hwif {
u8 __iomem *cfg_regs_base;
+ u8 __iomem *intr_regs_base;
+ u8 __iomem *mgmt_regs_base;
u64 db_base_phy;
u64 db_dwqe_len;
u8 __iomem *db_base;
DISABLE_OUTBOUND = 0x1,
};
+enum hinic3_pf_status {
+ HINIC3_PF_STATUS_INIT = 0x0,
+ HINIC3_PF_STATUS_ACTIVE_FLAG = 0x11,
+ HINIC3_PF_STATUS_FLR_START_FLAG = 0x12,
+ HINIC3_PF_STATUS_FLR_FINISH_FLAG = 0x13,
+};
+
enum hinic3_doorbell_ctrl {
ENABLE_DOORBELL = 0,
DISABLE_DOORBELL = 1,
HINIC3_SET_MSIX_AUTO_MASK,
};
+#define HINIC3_FUNC_TYPE(hwdev) ((hwdev)->hwif->attr.func_type)
+#define HINIC3_IS_PF(hwdev) \
+ (HINIC3_FUNC_TYPE(hwdev) == HINIC3_FUNC_TYPE_PF)
+#define HINIC3_IS_VF(hwdev) \
+ (HINIC3_FUNC_TYPE(hwdev) == HINIC3_FUNC_TYPE_VF)
+
u32 hinic3_hwif_read_reg(struct hinic3_hwif *hwif, u32 reg);
void hinic3_hwif_write_reg(struct hinic3_hwif *hwif, u32 reg, u32 val);
void __iomem **dwqe_base);
void hinic3_free_db_addr(struct hinic3_hwdev *hwdev, const u8 __iomem *db_base);
+void hinic3_set_pf_status(struct hinic3_hwif *hwif,
+ enum hinic3_pf_status status);
+enum hinic3_pf_status hinic3_get_pf_status(struct hinic3_hwif *hwif);
+
int hinic3_init_hwif(struct hinic3_hwdev *hwdev);
void hinic3_free_hwif(struct hinic3_hwdev *hwdev);
enum hinic3_msix_auto_mask flag);
u16 hinic3_global_func_id(struct hinic3_hwdev *hwdev);
+u8 hinic3_pf_id_of_vf(struct hinic3_hwdev *hwdev);
+u16 hinic3_glb_pf_vf_offset(struct hinic3_hwdev *hwdev);
+u8 hinic3_ppf_idx(struct hinic3_hwdev *hwdev);
#endif
#include <linux/iopoll.h>
#include "hinic3_hw_cfg.h"
+#include "hinic3_hw_comm.h"
#include "hinic3_hwdev.h"
+#include "hinic3_hwif.h"
#include "hinic3_lld.h"
#include "hinic3_mgmt.h"
#include "hinic3_pci_id_tbl.h"
#define HINIC3_VF_PCI_CFG_REG_BAR 0
+#define HINIC3_PF_PCI_CFG_REG_BAR 1
#define HINIC3_PCI_INTR_REG_BAR 2
+/* Only PF has mgmt bar */
+#define HINIC3_PCI_MGMT_REG_BAR 3
#define HINIC3_PCI_DB_BAR 4
+#define HINIC3_IS_VF_DEV(pdev) ((pdev)->device == PCI_DEV_ID_HINIC3_VF)
+
#define HINIC3_EVENT_POLL_SLEEP_US 1000
#define HINIC3_EVENT_POLL_TIMEOUT_US 10000000
static int hinic3_mapping_bar(struct pci_dev *pdev,
struct hinic3_pcidev *pci_adapter)
{
- pci_adapter->cfg_reg_base = pci_ioremap_bar(pdev,
- HINIC3_VF_PCI_CFG_REG_BAR);
+ int cfg_bar;
+
+ cfg_bar = HINIC3_IS_VF_DEV(pdev) ?
+ HINIC3_VF_PCI_CFG_REG_BAR : HINIC3_PF_PCI_CFG_REG_BAR;
+
+ pci_adapter->cfg_reg_base = pci_ioremap_bar(pdev, cfg_bar);
if (!pci_adapter->cfg_reg_base) {
dev_err(&pdev->dev, "Failed to map configuration regs\n");
return -ENOMEM;
goto err_unmap_cfg_reg_base;
}
+ if (!HINIC3_IS_VF_DEV(pdev)) {
+ pci_adapter->mgmt_reg_base =
+ pci_ioremap_bar(pdev, HINIC3_PCI_MGMT_REG_BAR);
+ if (!pci_adapter->mgmt_reg_base) {
+ dev_err(&pdev->dev, "Failed to map mgmt regs\n");
+ goto err_unmap_intr_reg_base;
+ }
+ }
+
pci_adapter->db_base_phy = pci_resource_start(pdev, HINIC3_PCI_DB_BAR);
pci_adapter->db_dwqe_len = pci_resource_len(pdev, HINIC3_PCI_DB_BAR);
pci_adapter->db_base = pci_ioremap_bar(pdev, HINIC3_PCI_DB_BAR);
if (!pci_adapter->db_base) {
dev_err(&pdev->dev, "Failed to map doorbell regs\n");
- goto err_unmap_intr_reg_base;
+ goto err_unmap_mgmt_reg_base;
}
return 0;
+err_unmap_mgmt_reg_base:
+ if (!HINIC3_IS_VF_DEV(pdev))
+ iounmap(pci_adapter->mgmt_reg_base);
err_unmap_intr_reg_base:
iounmap(pci_adapter->intr_reg_base);
static void hinic3_unmapping_bar(struct hinic3_pcidev *pci_adapter)
{
iounmap(pci_adapter->db_base);
+ if (!HINIC3_IS_VF_DEV(pci_adapter->pdev))
+ iounmap(pci_adapter->mgmt_reg_base);
iounmap(pci_adapter->intr_reg_base);
iounmap(pci_adapter->cfg_reg_base);
}
return err;
}
+ if (HINIC3_IS_PF(pci_adapter->hwdev))
+ hinic3_sync_time_to_fw(pci_adapter->hwdev);
+
err = hinic3_attach_aux_devices(pci_adapter->hwdev);
if (err)
goto err_free_hwdev;
{
struct hinic3_pcidev *pci_adapter = pci_get_drvdata(pdev);
+ /* disable mgmt reporting before flushing mgmt work-queue. */
+ hinic3_set_pf_status(pci_adapter->hwdev->hwif, HINIC3_PF_STATUS_INIT);
hinic3_flush_mgmt_workq(pci_adapter->hwdev);
hinic3_detach_aux_devices(pci_adapter->hwdev);
hinic3_free_hwdev(pci_adapter->hwdev);
static int hinic3_probe_func(struct hinic3_pcidev *pci_adapter)
{
struct pci_dev *pdev = pci_adapter->pdev;
+ struct comm_cmd_bdf_info bdf_info = {};
int err;
err = hinic3_mapping_bar(pdev, pci_adapter);
if (err)
goto err_unmap_bar;
+ if (HINIC3_IS_PF(pci_adapter->hwdev)) {
+ bdf_info.function_idx =
+ hinic3_global_func_id(pci_adapter->hwdev);
+ bdf_info.bus = pdev->bus->number;
+ bdf_info.device = PCI_SLOT(pdev->devfn);
+ bdf_info.function = PCI_FUNC(pdev->devfn);
+
+ err = hinic3_set_bdf_ctxt(pci_adapter->hwdev, &bdf_info);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to set BDF info to fw\n");
+ goto err_uninit_func;
+ }
+ }
+
return 0;
+err_uninit_func:
+ hinic3_func_uninit(pdev);
err_unmap_bar:
hinic3_unmapping_bar(pci_adapter);
{
struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
struct hinic3_hwdev *hwdev = nic_dev->hwdev;
+ u8 mac_addr[ETH_ALEN];
int err;
nic_dev->q_params.sq_depth = HINIC3_SQ_DEPTH;
hinic3_try_to_enable_rss(netdev);
- /* VF driver always uses random MAC address. During VM migration to a
- * new device, the new device should learn the VMs old MAC rather than
- * provide its own MAC. The product design assumes that every VF is
- * suspectable to migration so the device avoids offering MAC address
- * to VFs.
- */
- eth_hw_addr_random(netdev);
+ if (HINIC3_IS_VF(hwdev)) {
+ /* VF driver always uses random MAC address. During VM migration
+ * to a new device, the new device should learn the VMs old MAC
+ * rather than provide its own MAC. The product design assumes
+ * that every VF is susceptible to migration so the device
+ * avoids offering MAC address to VFs.
+ */
+ eth_hw_addr_random(netdev);
+ } else {
+ err = hinic3_get_default_mac(hwdev, mac_addr);
+ if (err) {
+ dev_err(hwdev->dev, "Failed to get MAC address\n");
+ goto err_clear_rss_config;
+ }
+ eth_hw_addr_set(netdev, mac_addr);
+ }
+
err = hinic3_set_mac(hwdev, netdev->dev_addr, 0,
hinic3_global_func_id(hwdev));
- if (err) {
+ /* Failure to set MAC is not a fatal error for VF since its MAC may have
+ * already been set by PF
+ */
+ if (err && err != -EADDRINUSE) {
dev_err(hwdev->dev, "Failed to set default MAC\n");
goto err_clear_rss_config;
}
enum mbox_msg_direction_type dir,
u16 src_func_id)
{
+ struct hinic3_hwdev *hwdev = mbox->hwdev;
struct hinic3_msg_channel *msg_ch;
- msg_ch = (src_func_id == MBOX_MGMT_FUNC_ID) ?
- &mbox->mgmt_msg : mbox->func_msg;
+ if (src_func_id == MBOX_MGMT_FUNC_ID) {
+ msg_ch = &mbox->mgmt_msg;
+ } else if (HINIC3_IS_VF(hwdev)) {
+ /* message from pf */
+ msg_ch = mbox->func_msg;
+ if (src_func_id != hinic3_pf_id_of_vf(hwdev) || !msg_ch)
+ return NULL;
+ } else {
+ return NULL;
+ }
return (dir == MBOX_MSG_SEND) ?
&msg_ch->recv_msg : &msg_ch->resp_msg;
dir = MBOX_MSG_HEADER_GET(mbox_header, DIRECTION);
src_func_id = MBOX_MSG_HEADER_GET(mbox_header, SRC_GLB_FUNC_IDX);
msg_desc = get_mbox_msg_desc(mbox, dir, src_func_id);
+ if (!msg_desc) {
+ dev_err(mbox->hwdev->dev,
+ "Mailbox source function id: %u is invalid for current function\n",
+ src_func_id);
+ return;
+ }
recv_mbox_handler(mbox, header, msg_desc);
}
if (err)
goto err_destroy_workqueue;
- err = hinic3_init_func_mbox_msg_channel(hwdev);
- if (err)
- goto err_uninit_mgmt_msg_ch;
+ if (HINIC3_IS_VF(hwdev)) {
+ /* VF to PF mbox message channel */
+ err = hinic3_init_func_mbox_msg_channel(hwdev);
+ if (err)
+ goto err_uninit_mgmt_msg_ch;
+ }
err = alloc_mbox_wb_status(mbox);
if (err) {
return 0;
err_uninit_func_mbox_msg_ch:
- hinic3_uninit_func_mbox_msg_channel(hwdev);
-
+ if (HINIC3_IS_VF(hwdev))
+ hinic3_uninit_func_mbox_msg_channel(hwdev);
err_uninit_mgmt_msg_ch:
uninit_mgmt_msg_channel(mbox);
{
struct hinic3_hwif *hwif = mbox->hwdev->hwif;
u32 mbox_int, mbox_ctrl, tx_size;
+ u16 func = dst_func;
+ /* VF can send non-management messages only to PF. We set DST_FUNC field
+ * to 0 since HW will ignore it anyway.
+ */
+ if (HINIC3_IS_VF(mbox->hwdev) && dst_func != MBOX_MGMT_FUNC_ID)
+ func = 0;
tx_size = ALIGN(seg_len + MBOX_HEADER_SZ, MBOX_SEG_LEN_ALIGN) >> 2;
mbox_int = MBOX_INT_SET(dst_aeqn, DST_AEQN) |
mbox_ctrl = MBOX_CTRL_SET(1, TX_STATUS) |
MBOX_CTRL_SET(0, TRIGGER_AEQE) |
- MBOX_CTRL_SET(dst_func, DST_FUNC);
+ MBOX_CTRL_SET(func, DST_FUNC);
hinic3_hwif_write_reg(hwif, HINIC3_FUNC_CSR_MAILBOX_INT_OFF, mbox_int);
hinic3_hwif_write_reg(hwif, HINIC3_FUNC_CSR_MAILBOX_CONTROL_OFF,
L2NIC_CMD_SET_SQ_CI_ATTR = 8,
L2NIC_CMD_CLEAR_QP_RESOURCE = 11,
L2NIC_CMD_FEATURE_NEGO = 15,
+ L2NIC_CMD_GET_MAC = 20,
L2NIC_CMD_SET_MAC = 21,
L2NIC_CMD_DEL_MAC = 22,
L2NIC_CMD_UPDATE_MAC = 23,
&func_tbl_cfg);
}
+static bool hinic3_check_vf_set_by_pf(struct hinic3_hwdev *hwdev,
+ u8 status)
+{
+ return HINIC3_IS_VF(hwdev) && status == HINIC3_PF_SET_VF_ALREADY;
+}
+
static int hinic3_check_mac_info(struct hinic3_hwdev *hwdev, u8 status,
u16 vlan_id)
{
if ((status && status != MGMT_STATUS_EXIST) ||
((vlan_id & BIT(15)) && status == MGMT_STATUS_EXIST)) {
+ if (hinic3_check_vf_set_by_pf(hwdev, status))
+ return 0;
+
return -EINVAL;
}
return 0;
}
+int hinic3_get_default_mac(struct hinic3_hwdev *hwdev, u8 *mac_addr)
+{
+ struct l2nic_cmd_set_mac mac_info = {};
+ struct mgmt_msg_params msg_params = {};
+ int err;
+
+ mac_info.func_id = hinic3_global_func_id(hwdev);
+
+ mgmt_msg_params_init_default(&msg_params, &mac_info, sizeof(mac_info));
+
+ err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+ L2NIC_CMD_GET_MAC,
+ &msg_params);
+
+ if (err || mac_info.msg_head.status) {
+ dev_err(hwdev->dev,
+ "Failed to get mac, err: %d, status: 0x%x\n",
+ err, mac_info.msg_head.status);
+ return -EFAULT;
+ }
+
+ ether_addr_copy(mac_addr, mac_info.mac);
+
+ return 0;
+}
+
int hinic3_set_mac(struct hinic3_hwdev *hwdev, const u8 *mac_addr, u16 vlan_id,
u16 func_id)
{
return -EIO;
}
- if (mac_info.msg_head.status == MGMT_STATUS_PF_SET_VF_ALREADY) {
+ if (hinic3_check_vf_set_by_pf(hwdev, mac_info.msg_head.status)) {
dev_warn(hwdev->dev, "PF has already set VF mac, Ignore set operation\n");
- return 0;
+ return -EADDRINUSE;
}
if (mac_info.msg_head.status == MGMT_STATUS_EXIST) {
err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
L2NIC_CMD_DEL_MAC, &msg_params);
- if (err) {
+ if (err ||
+ (mac_info.msg_head.status &&
+ !hinic3_check_vf_set_by_pf(hwdev, mac_info.msg_head.status))) {
dev_err(hwdev->dev,
"Failed to delete MAC, err: %d, status: 0x%x\n",
err, mac_info.msg_head.status);
- return err;
+ return -EFAULT;
+ }
+
+ if (hinic3_check_vf_set_by_pf(hwdev, mac_info.msg_head.status)) {
+ dev_warn(hwdev->dev, "PF has already set VF mac, Ignore delete operation.\n");
+ return -EADDRINUSE;
}
return 0;
return -EIO;
}
+ if (hinic3_check_vf_set_by_pf(hwdev, mac_info.msg_head.status)) {
+ dev_warn(hwdev->dev, "PF has already set VF MAC. Ignore update operation\n");
+ return -EADDRINUSE;
+ }
+
+ if (mac_info.msg_head.status == HINIC3_MGMT_STATUS_EXIST) {
+ dev_warn(hwdev->dev,
+ "MAC is repeated. Ignore update operation\n");
+ return 0;
+ }
+
return 0;
}
#define HINIC3_MAX_JUMBO_FRAME_SIZE 9600
#define HINIC3_VLAN_ID_MASK 0x7FFF
+#define HINIC3_PF_SET_VF_ALREADY 0x4
+#define HINIC3_MGMT_STATUS_EXIST 0x6
enum hinic3_nic_event_type {
HINIC3_NIC_EVENT_LINK_DOWN = 0,
int hinic3_init_function_table(struct hinic3_nic_dev *nic_dev);
int hinic3_set_port_mtu(struct net_device *netdev, u16 new_mtu);
+int hinic3_get_default_mac(struct hinic3_hwdev *hwdev, u8 *mac_addr);
int hinic3_set_mac(struct hinic3_hwdev *hwdev, const u8 *mac_addr, u16 vlan_id,
u16 func_id);
int hinic3_del_mac(struct hinic3_hwdev *hwdev, const u8 *mac_addr, u16 vlan_id,