diff options
author | Paolo Abeni <pabeni@redhat.com> | 2023-12-19 12:00:55 +0100 |
---|---|---|
committer | Paolo Abeni <pabeni@redhat.com> | 2023-12-19 12:00:55 +0100 |
commit | f7dd48ea76be30666f0614d6a06061185ed38c60 (patch) | |
tree | b40d4aae33c1990b99a4b2277d6a1aeb638d312d | |
parent | c49b292d031e385abf764ded32cd953c77e73f2d (diff) | |
parent | 4ebb86a97ceb948b32cbeafb598617a6c8748376 (diff) | |
download | linux-f7dd48ea76be30666f0614d6a06061185ed38c60.tar.gz linux-f7dd48ea76be30666f0614d6a06061185ed38c60.tar.bz2 linux-f7dd48ea76be30666f0614d6a06061185ed38c60.zip |
Merge branch 'add-pf-vf-mailbox-support'
Shinas Rasheed says:
====================
add PF-VF mailbox support
This patchset aims to add PF-VF mailbox support, its related
version support, and relevant control net support for immediate
functionalities such as firmware notifications to VF.
Changes:
V6:
- Fixed 1/4 patch to apply to top of net-next merged with net fixes
V5: https://lore.kernel.org/all/20231214164536.2670006-1-srasheed@marvell.com/
- Refactored patches to cut out redundant changes in 1/4 patch.
V4: https://lore.kernel.org/all/20231213035816.2656851-1-srasheed@marvell.com/
- Included tag [1/4] in subject of first patch of series which was
lost in V3
V3: https://lore.kernel.org/all/20231211063355.2630028-1-srasheed@marvell.com/
- Corrected error cleanup logic for PF-VF mbox setup
- Removed double inclusion of types.h header file in octep_pfvf_mbox.c
V2: https://lore.kernel.org/all/20231209081450.2613561-1-srasheed@marvell.com/
- Removed unused variable in PATCH 1/4
V1: https://lore.kernel.org/all/20231208070352.2606192-1-srasheed@marvell.com/
====================
Link: https://lore.kernel.org/r/20231215181425.2681426-1-srasheed@marvell.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
12 files changed, 859 insertions, 49 deletions
diff --git a/drivers/net/ethernet/marvell/octeon_ep/Makefile b/drivers/net/ethernet/marvell/octeon_ep/Makefile index 02a4a21bc298..62162ed63f34 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/Makefile +++ b/drivers/net/ethernet/marvell/octeon_ep/Makefile @@ -7,4 +7,4 @@ obj-$(CONFIG_OCTEON_EP) += octeon_ep.o octeon_ep-y := octep_main.o octep_cn9k_pf.o octep_tx.o octep_rx.o \ octep_ethtool.o octep_ctrl_mbox.o octep_ctrl_net.o \ - octep_cnxk_pf.o + octep_pfvf_mbox.o octep_cnxk_pf.o diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c b/drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c index 9209f1ec1b52..b5805969404f 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c @@ -362,16 +362,55 @@ static void octep_setup_mbox_regs_cn93_pf(struct octep_device *oct, int q_no) { struct octep_mbox *mbox = oct->mbox[q_no]; - mbox->q_no = q_no; - - /* PF mbox interrupt reg */ - mbox->mbox_int_reg = oct->mmio[0].hw_addr + CN93_SDP_EPF_MBOX_RINT(0); - /* PF to VF DATA reg. PF writes into this reg */ - mbox->mbox_write_reg = oct->mmio[0].hw_addr + CN93_SDP_R_MBOX_PF_VF_DATA(q_no); + mbox->pf_vf_data_reg = oct->mmio[0].hw_addr + CN93_SDP_MBOX_PF_VF_DATA(q_no); /* VF to PF DATA reg. PF reads from this reg */ - mbox->mbox_read_reg = oct->mmio[0].hw_addr + CN93_SDP_R_MBOX_VF_PF_DATA(q_no); + mbox->vf_pf_data_reg = oct->mmio[0].hw_addr + CN93_SDP_MBOX_VF_PF_DATA(q_no); +} + +/* Poll for mailbox messages from VF */ +static void octep_poll_pfvf_mailbox(struct octep_device *oct) +{ + u32 vf, active_vfs, active_rings_per_vf, vf_mbox_queue; + u64 reg0, reg1; + + reg0 = octep_read_csr64(oct, CN93_SDP_EPF_MBOX_RINT(0)); + reg1 = octep_read_csr64(oct, CN93_SDP_EPF_MBOX_RINT(1)); + if (reg0 || reg1) { + active_vfs = CFG_GET_ACTIVE_VFS(oct->conf); + active_rings_per_vf = CFG_GET_ACTIVE_RPVF(oct->conf); + for (vf = 0; vf < active_vfs; vf++) { + vf_mbox_queue = vf * active_rings_per_vf; + + if (vf_mbox_queue < 64) { + if (!(reg0 & (0x1UL << vf_mbox_queue))) + continue; + } else { + if (!(reg1 & (0x1UL << (vf_mbox_queue - 64)))) + continue; + } + + if (!oct->mbox[vf_mbox_queue]) { + dev_err(&oct->pdev->dev, "bad mbox vf %d\n", vf); + continue; + } + schedule_work(&oct->mbox[vf_mbox_queue]->wk.work); + } + if (reg0) + octep_write_csr64(oct, CN93_SDP_EPF_MBOX_RINT(0), reg0); + if (reg1) + octep_write_csr64(oct, CN93_SDP_EPF_MBOX_RINT(1), reg1); + } +} + +/* PF-VF mailbox interrupt handler */ +static irqreturn_t octep_pfvf_mbox_intr_handler_cn93_pf(void *dev) +{ + struct octep_device *oct = (struct octep_device *)dev; + + octep_poll_pfvf_mailbox(oct); + return IRQ_HANDLED; } /* Poll OEI events like heartbeat */ @@ -403,6 +442,7 @@ static irqreturn_t octep_oei_intr_handler_cn93_pf(void *dev) */ static void octep_poll_non_ioq_interrupts_cn93_pf(struct octep_device *oct) { + octep_poll_pfvf_mailbox(oct); octep_poll_oei_cn93_pf(oct); } @@ -646,6 +686,8 @@ static void octep_enable_interrupts_cn93_pf(struct octep_device *oct) octep_write_csr64(oct, CN93_SDP_EPF_MISC_RINT_ENA_W1S, intr_mask); octep_write_csr64(oct, CN93_SDP_EPF_DMA_RINT_ENA_W1S, intr_mask); + octep_write_csr64(oct, CN93_SDP_EPF_MBOX_RINT_ENA_W1S(0), -1ULL); + octep_write_csr64(oct, CN93_SDP_EPF_MBOX_RINT_ENA_W1S(1), -1ULL); octep_write_csr64(oct, CN93_SDP_EPF_DMA_VF_RINT_ENA_W1S(0), -1ULL); octep_write_csr64(oct, CN93_SDP_EPF_PP_VF_RINT_ENA_W1S(0), -1ULL); @@ -672,6 +714,8 @@ static void octep_disable_interrupts_cn93_pf(struct octep_device *oct) octep_write_csr64(oct, CN93_SDP_EPF_MISC_RINT_ENA_W1C, intr_mask); octep_write_csr64(oct, CN93_SDP_EPF_DMA_RINT_ENA_W1C, intr_mask); + octep_write_csr64(oct, CN93_SDP_EPF_MBOX_RINT_ENA_W1C(0), -1ULL); + octep_write_csr64(oct, CN93_SDP_EPF_MBOX_RINT_ENA_W1C(1), -1ULL); octep_write_csr64(oct, CN93_SDP_EPF_DMA_VF_RINT_ENA_W1C(0), -1ULL); octep_write_csr64(oct, CN93_SDP_EPF_PP_VF_RINT_ENA_W1C(0), -1ULL); @@ -807,6 +851,7 @@ void octep_device_setup_cn93_pf(struct octep_device *oct) oct->hw_ops.setup_oq_regs = octep_setup_oq_regs_cn93_pf; oct->hw_ops.setup_mbox_regs = octep_setup_mbox_regs_cn93_pf; + oct->hw_ops.mbox_intr_handler = octep_pfvf_mbox_intr_handler_cn93_pf; oct->hw_ops.oei_intr_handler = octep_oei_intr_handler_cn93_pf; oct->hw_ops.ire_intr_handler = octep_ire_intr_handler_cn93_pf; oct->hw_ops.ore_intr_handler = octep_ore_intr_handler_cn93_pf; diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c b/drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c index 098a0c5c4d1c..5de0b5ecbc5f 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c @@ -392,16 +392,44 @@ static void octep_setup_mbox_regs_cnxk_pf(struct octep_device *oct, int q_no) { struct octep_mbox *mbox = oct->mbox[q_no]; - mbox->q_no = q_no; - - /* PF mbox interrupt reg */ - mbox->mbox_int_reg = oct->mmio[0].hw_addr + CNXK_SDP_EPF_MBOX_RINT(0); - /* PF to VF DATA reg. PF writes into this reg */ - mbox->mbox_write_reg = oct->mmio[0].hw_addr + CNXK_SDP_R_MBOX_PF_VF_DATA(q_no); + mbox->pf_vf_data_reg = oct->mmio[0].hw_addr + CNXK_SDP_MBOX_PF_VF_DATA(q_no); /* VF to PF DATA reg. PF reads from this reg */ - mbox->mbox_read_reg = oct->mmio[0].hw_addr + CNXK_SDP_R_MBOX_VF_PF_DATA(q_no); + mbox->vf_pf_data_reg = oct->mmio[0].hw_addr + CNXK_SDP_MBOX_VF_PF_DATA(q_no); +} + +static void octep_poll_pfvf_mailbox_cnxk_pf(struct octep_device *oct) +{ + u32 vf, active_vfs, active_rings_per_vf, vf_mbox_queue; + u64 reg0; + + reg0 = octep_read_csr64(oct, CNXK_SDP_EPF_MBOX_RINT(0)); + if (reg0) { + active_vfs = CFG_GET_ACTIVE_VFS(oct->conf); + active_rings_per_vf = CFG_GET_ACTIVE_RPVF(oct->conf); + for (vf = 0; vf < active_vfs; vf++) { + vf_mbox_queue = vf * active_rings_per_vf; + if (!(reg0 & (0x1UL << vf_mbox_queue))) + continue; + + if (!oct->mbox[vf_mbox_queue]) { + dev_err(&oct->pdev->dev, "bad mbox vf %d\n", vf); + continue; + } + schedule_work(&oct->mbox[vf_mbox_queue]->wk.work); + } + if (reg0) + octep_write_csr64(oct, CNXK_SDP_EPF_MBOX_RINT(0), reg0); + } +} + +static irqreturn_t octep_pfvf_mbox_intr_handler_cnxk_pf(void *dev) +{ + struct octep_device *oct = (struct octep_device *)dev; + + octep_poll_pfvf_mailbox_cnxk_pf(oct); + return IRQ_HANDLED; } /* Poll OEI events like heartbeat */ @@ -435,6 +463,7 @@ static irqreturn_t octep_oei_intr_handler_cnxk_pf(void *dev) */ static void octep_poll_non_ioq_interrupts_cnxk_pf(struct octep_device *oct) { + octep_poll_pfvf_mailbox_cnxk_pf(oct); octep_poll_oei_cnxk_pf(oct); } @@ -682,6 +711,7 @@ static void octep_enable_interrupts_cnxk_pf(struct octep_device *oct) octep_write_csr64(oct, CNXK_SDP_EPF_MISC_RINT_ENA_W1S, intr_mask); octep_write_csr64(oct, CNXK_SDP_EPF_DMA_RINT_ENA_W1S, intr_mask); + octep_write_csr64(oct, CNXK_SDP_EPF_MBOX_RINT_ENA_W1S(0), -1ULL); octep_write_csr64(oct, CNXK_SDP_EPF_DMA_VF_RINT_ENA_W1S(0), -1ULL); octep_write_csr64(oct, CNXK_SDP_EPF_PP_VF_RINT_ENA_W1S(0), -1ULL); @@ -708,6 +738,7 @@ static void octep_disable_interrupts_cnxk_pf(struct octep_device *oct) octep_write_csr64(oct, CNXK_SDP_EPF_MISC_RINT_ENA_W1C, intr_mask); octep_write_csr64(oct, CNXK_SDP_EPF_DMA_RINT_ENA_W1C, intr_mask); + octep_write_csr64(oct, CNXK_SDP_EPF_MBOX_RINT_ENA_W1C(0), -1ULL); octep_write_csr64(oct, CNXK_SDP_EPF_DMA_VF_RINT_ENA_W1C(0), -1ULL); octep_write_csr64(oct, CNXK_SDP_EPF_PP_VF_RINT_ENA_W1C(0), -1ULL); @@ -843,6 +874,7 @@ void octep_device_setup_cnxk_pf(struct octep_device *oct) oct->hw_ops.setup_oq_regs = octep_setup_oq_regs_cnxk_pf; oct->hw_ops.setup_mbox_regs = octep_setup_mbox_regs_cnxk_pf; + oct->hw_ops.mbox_intr_handler = octep_pfvf_mbox_intr_handler_cnxk_pf; oct->hw_ops.oei_intr_handler = octep_oei_intr_handler_cnxk_pf; oct->hw_ops.ire_intr_handler = octep_ire_intr_handler_cnxk_pf; oct->hw_ops.ore_intr_handler = octep_ore_intr_handler_cnxk_pf; diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.h b/drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.h index 7f8135788efc..6da32d40f926 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.h +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.h @@ -16,10 +16,12 @@ * |reserved (4 bytes) | * |-------------------------------------------| * |host version (8 bytes) | + * | low 32 bits | * |host status (8 bytes) | * |host reserved (104 bytes) | * |-------------------------------------------| - * |fw version (8 bytes) | + * |fw version's (8 bytes) | + * | min=high 32 bits, max=low 32 bits | * |fw status (8 bytes) | * |fw reserved (104 bytes) | * |===========================================| diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_net.c b/drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_net.c index 9dff2166dbb7..01b7be154c38 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_net.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_net.c @@ -13,6 +13,7 @@ #include "octep_config.h" #include "octep_main.h" #include "octep_ctrl_net.h" +#include "octep_pfvf_mbox.h" /* Control plane version */ #define OCTEP_CP_VERSION_CURRENT OCTEP_CP_VERSION(1, 0, 0) @@ -329,6 +330,11 @@ static int process_mbox_notify(struct octep_device *oct, octep_ctrl_net_f2h_cmd_versions[cmd] < OCTEP_CP_VERSION_CURRENT) return -EOPNOTSUPP; + if (msg->hdr.s.is_vf) { + octep_pfvf_notify(oct, msg); + return 0; + } + switch (cmd) { case OCTEP_CTRL_NET_F2H_CMD_LINK_STATUS: if (netif_running(netdev)) { diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c index e56188f1d09b..7c9faa714a10 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c @@ -16,6 +16,7 @@ #include "octep_config.h" #include "octep_main.h" #include "octep_ctrl_net.h" +#include "octep_pfvf_mbox.h" #define OCTEP_INTR_POLL_TIME_MSECS 100 struct workqueue_struct *octep_wq; @@ -160,6 +161,21 @@ static void octep_disable_msix(struct octep_device *oct) } /** + * octep_mbox_intr_handler() - common handler for pfvf mbox interrupts. + * + * @irq: Interrupt number. + * @data: interrupt data. + * + * this is common handler for pfvf mbox interrupts. + */ +static irqreturn_t octep_mbox_intr_handler(int irq, void *data) +{ + struct octep_device *oct = data; + + return oct->hw_ops.mbox_intr_handler(oct); +} + +/** * octep_oei_intr_handler() - common handler for output endpoint interrupts. * * @irq: Interrupt number. @@ -362,8 +378,12 @@ static int octep_request_irqs(struct octep_device *oct) snprintf(irq_name, OCTEP_MSIX_NAME_SIZE, "%s-%s", netdev->name, non_ioq_msix_names[i]); - if (!strncmp(non_ioq_msix_names[i], "epf_oei_rint", - strlen("epf_oei_rint"))) { + if (!strncmp(non_ioq_msix_names[i], "epf_mbox_rint", strlen("epf_mbox_rint"))) { + ret = request_irq(msix_entry->vector, + octep_mbox_intr_handler, 0, + irq_name, oct); + } else if (!strncmp(non_ioq_msix_names[i], "epf_oei_rint", + strlen("epf_oei_rint"))) { ret = request_irq(msix_entry->vector, octep_oei_intr_handler, 0, irq_name, oct); @@ -1322,6 +1342,7 @@ static void octep_device_cleanup(struct octep_device *oct) oct->mbox[i] = NULL; } + octep_delete_pfvf_mbox(oct); octep_ctrl_net_uninit(oct); cancel_delayed_work_sync(&oct->hb_task); @@ -1419,6 +1440,12 @@ static int octep_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_octep_config; } + err = octep_setup_pfvf_mbox(octep_dev); + if (err) { + dev_err(&pdev->dev, "PF-VF mailbox setup failed\n"); + goto register_dev_err; + } + err = octep_ctrl_net_get_info(octep_dev, OCTEP_CTRL_NET_INVALID_VFID, &octep_dev->conf->fw_info); if (err) { @@ -1487,6 +1514,21 @@ err_dma_mask: return err; } +static int octep_sriov_disable(struct octep_device *oct) +{ + struct pci_dev *pdev = oct->pdev; + + if (pci_vfs_assigned(oct->pdev)) { + dev_warn(&pdev->dev, "Can't disable SRIOV while VFs are assigned\n"); + return -EPERM; + } + + pci_disable_sriov(pdev); + CFG_GET_ACTIVE_VFS(oct->conf) = 0; + + return 0; +} + /** * octep_remove() - Remove Octeon PCI device from driver control. * @@ -1504,6 +1546,7 @@ static void octep_remove(struct pci_dev *pdev) return; netdev = oct->netdev; + octep_sriov_disable(oct); if (netdev->reg_state == NETREG_REGISTERED) unregister_netdev(netdev); @@ -1514,11 +1557,47 @@ static void octep_remove(struct pci_dev *pdev) pci_disable_device(pdev); } +static int octep_sriov_enable(struct octep_device *oct, int num_vfs) +{ + struct pci_dev *pdev = oct->pdev; + int err; + + CFG_GET_ACTIVE_VFS(oct->conf) = num_vfs; + err = pci_enable_sriov(pdev, num_vfs); + if (err) { + dev_warn(&pdev->dev, "Failed to enable SRIOV err=%d\n", err); + CFG_GET_ACTIVE_VFS(oct->conf) = 0; + return err; + } + + return num_vfs; +} + +static int octep_sriov_configure(struct pci_dev *pdev, int num_vfs) +{ + struct octep_device *oct = pci_get_drvdata(pdev); + int max_nvfs; + + if (num_vfs == 0) + return octep_sriov_disable(oct); + + max_nvfs = CFG_GET_MAX_VFS(oct->conf); + + if (num_vfs > max_nvfs) { + dev_err(&pdev->dev, "Invalid VF count Max supported VFs = %d\n", + max_nvfs); + return -EINVAL; + } + + return octep_sriov_enable(oct, num_vfs); +} + static struct pci_driver octep_driver = { .name = OCTEP_DRV_NAME, .id_table = octep_pci_id_tbl, .probe = octep_probe, .remove = octep_remove, + .sriov_configure = octep_sriov_configure, }; /** diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_main.h b/drivers/net/ethernet/marvell/octeon_ep/octep_main.h index 2cb93d425744..fee59e0e0138 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_main.h +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_main.h @@ -80,6 +80,7 @@ struct octep_hw_ops { void (*setup_oq_regs)(struct octep_device *oct, int q); void (*setup_mbox_regs)(struct octep_device *oct, int mbox); + irqreturn_t (*mbox_intr_handler)(void *ioq_vector); irqreturn_t (*oei_intr_handler)(void *ioq_vector); irqreturn_t (*ire_intr_handler)(void *ioq_vector); irqreturn_t (*ore_intr_handler)(void *ioq_vector); @@ -118,28 +119,27 @@ struct octep_mbox_data { u64 *data; }; +#define MAX_VF_PF_MBOX_DATA_SIZE 384 +/* wrappers around work structs */ +struct octep_pfvf_mbox_wk { + struct work_struct work; + void *ctxptr; + u64 ctxul; +}; + /* Octeon device mailbox */ struct octep_mbox { - /* A spinlock to protect access to this q_mbox. */ - spinlock_t lock; - - u32 q_no; - u32 state; - - /* SLI_MAC_PF_MBOX_INT for PF, SLI_PKT_MBOX_INT for VF. */ - u8 __iomem *mbox_int_reg; - - /* SLI_PKT_PF_VF_MBOX_SIG(0) for PF, - * SLI_PKT_PF_VF_MBOX_SIG(1) for VF. - */ - u8 __iomem *mbox_write_reg; - - /* SLI_PKT_PF_VF_MBOX_SIG(1) for PF, - * SLI_PKT_PF_VF_MBOX_SIG(0) for VF. - */ - u8 __iomem *mbox_read_reg; - + /* A mutex to protect access to this q_mbox. */ + struct mutex lock; + u32 vf_id; + u32 config_data_index; + u32 message_len; + u8 __iomem *pf_vf_data_reg; + u8 __iomem *vf_pf_data_reg; + struct octep_pfvf_mbox_wk wk; + struct octep_device *oct; struct octep_mbox_data mbox_data; + u8 config_data[MAX_VF_PF_MBOX_DATA_SIZE]; }; /* Tx/Rx queue vector per interrupt. */ @@ -217,6 +217,12 @@ struct octep_iface_link_info { u8 oper_up; }; +/* The Octeon VF device specific info data structure.*/ +struct octep_pfvf_info { + u8 mac_addr[ETH_ALEN]; + u32 mbox_version; +}; + /* The Octeon device specific private data structure. * Each Octeon device has this structure to represent all its components. */ @@ -282,6 +288,8 @@ struct octep_device { /* Mailbox to talk to VFs */ struct octep_mbox *mbox[OCTEP_MAX_VF]; + /* VFs info */ + struct octep_pfvf_info vf_info[OCTEP_MAX_VF]; /* Work entry to handle Tx timeout */ struct work_struct tx_timeout_task; diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_pfvf_mbox.c b/drivers/net/ethernet/marvell/octeon_ep/octep_pfvf_mbox.c new file mode 100644 index 000000000000..2e2c3be8a0b4 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_pfvf_mbox.c @@ -0,0 +1,449 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell Octeon EP (EndPoint) Ethernet Driver + * + * Copyright (C) 2020 Marvell. + * + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mutex.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/io.h> +#include <linux/pci.h> +#include <linux/etherdevice.h> + +#include "octep_config.h" +#include "octep_main.h" +#include "octep_pfvf_mbox.h" +#include "octep_ctrl_net.h" + +/* When a new command is implemented, the below table should be updated + * with new command and it's version info. + */ +static u32 pfvf_cmd_versions[OCTEP_PFVF_MBOX_CMD_MAX] = { + [0 ... OCTEP_PFVF_MBOX_CMD_DEV_REMOVE] = OCTEP_PFVF_MBOX_VERSION_V1, + [OCTEP_PFVF_MBOX_CMD_GET_FW_INFO ... OCTEP_PFVF_MBOX_NOTIF_LINK_STATUS] = + OCTEP_PFVF_MBOX_VERSION_V2 +}; + +static void octep_pfvf_validate_version(struct octep_device *oct, u32 vf_id, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + u32 vf_version = (u32)cmd.s_version.version; + + dev_dbg(&oct->pdev->dev, "VF id:%d VF version:%d PF version:%d\n", + vf_id, vf_version, OCTEP_PFVF_MBOX_VERSION_CURRENT); + if (vf_version < OCTEP_PFVF_MBOX_VERSION_CURRENT) + rsp->s_version.version = vf_version; + else + rsp->s_version.version = OCTEP_PFVF_MBOX_VERSION_CURRENT; + + oct->vf_info[vf_id].mbox_version = rsp->s_version.version; + dev_dbg(&oct->pdev->dev, "VF id:%d negotiated VF version:%d\n", + vf_id, oct->vf_info[vf_id].mbox_version); + + rsp->s_version.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; +} + +static void octep_pfvf_get_link_status(struct octep_device *oct, u32 vf_id, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + int status; + + status = octep_ctrl_net_get_link_status(oct, vf_id); + if (status < 0) { + rsp->s_link_status.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; + dev_err(&oct->pdev->dev, "Get VF link status failed via host control Mbox\n"); + return; + } + rsp->s_link_status.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; + rsp->s_link_status.status = status; +} + +static void octep_pfvf_set_link_status(struct octep_device *oct, u32 vf_id, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + int err; + + err = octep_ctrl_net_set_link_status(oct, vf_id, cmd.s_link_status.status, true); + if (err) { + rsp->s_link_status.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; + dev_err(&oct->pdev->dev, "Set VF link status failed via host control Mbox\n"); + return; + } + rsp->s_link_status.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; +} + +static void octep_pfvf_set_rx_state(struct octep_device *oct, u32 vf_id, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + int err; + + err = octep_ctrl_net_set_rx_state(oct, vf_id, cmd.s_link_state.state, true); + if (err) { + rsp->s_link_state.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; + dev_err(&oct->pdev->dev, "Set VF Rx link state failed via host control Mbox\n"); + return; + } + rsp->s_link_state.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; +} + +static int octep_send_notification(struct octep_device *oct, u32 vf_id, + union octep_pfvf_mbox_word cmd) +{ + u32 max_rings_per_vf, vf_mbox_queue; + struct octep_mbox *mbox; + + /* check if VF PF Mailbox is compatible for this notification */ + if (pfvf_cmd_versions[cmd.s.opcode] > oct->vf_info[vf_id].mbox_version) { + dev_dbg(&oct->pdev->dev, "VF Mbox doesn't support Notification:%d on VF ver:%d\n", + cmd.s.opcode, oct->vf_info[vf_id].mbox_version); + return -EOPNOTSUPP; + } + + max_rings_per_vf = CFG_GET_MAX_RPVF(oct->conf); + vf_mbox_queue = vf_id * max_rings_per_vf; + if (!oct->mbox[vf_mbox_queue]) { + dev_err(&oct->pdev->dev, "Notif obtained for bad mbox vf %d\n", vf_id); + return -EINVAL; + } + mbox = oct->mbox[vf_mbox_queue]; + + mutex_lock(&mbox->lock); + writeq(cmd.u64, mbox->pf_vf_data_reg); + mutex_unlock(&mbox->lock); + + return 0; +} + +static void octep_pfvf_set_mtu(struct octep_device *oct, u32 vf_id, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + int err; + + err = octep_ctrl_net_set_mtu(oct, vf_id, cmd.s_set_mtu.mtu, true); + if (err) { + rsp->s_set_mtu.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; + dev_err(&oct->pdev->dev, "Set VF MTU failed via host control Mbox\n"); + return; + } + rsp->s_set_mtu.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; +} + +static void octep_pfvf_get_mtu(struct octep_device *oct, u32 vf_id, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + int max_rx_pktlen = oct->netdev->max_mtu + (ETH_HLEN + ETH_FCS_LEN); + + rsp->s_set_mtu.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; + rsp->s_get_mtu.mtu = max_rx_pktlen; +} + +static void octep_pfvf_set_mac_addr(struct octep_device *oct, u32 vf_id, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + int err; + + err = octep_ctrl_net_set_mac_addr(oct, vf_id, cmd.s_set_mac.mac_addr, true); + if (err) { + rsp->s_set_mac.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; + dev_err(&oct->pdev->dev, "Set VF MAC address failed via host control Mbox\n"); + return; + } + rsp->s_set_mac.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; +} + +static void octep_pfvf_get_mac_addr(struct octep_device *oct, u32 vf_id, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + int err; + + err = octep_ctrl_net_get_mac_addr(oct, vf_id, rsp->s_set_mac.mac_addr); + if (err) { + rsp->s_set_mac.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; + dev_err(&oct->pdev->dev, "Get VF MAC address failed via host control Mbox\n"); + return; + } + rsp->s_set_mac.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; +} + +static void octep_pfvf_dev_remove(struct octep_device *oct, u32 vf_id, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + int err; + + err = octep_ctrl_net_dev_remove(oct, vf_id); + if (err) { + rsp->s.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; + dev_err(&oct->pdev->dev, "Failed to acknowledge fw of vf %d removal\n", + vf_id); + return; + } + rsp->s.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; +} + +static void octep_pfvf_get_fw_info(struct octep_device *oct, u32 vf_id, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + struct octep_fw_info fw_info; + int err; + + err = octep_ctrl_net_get_info(oct, vf_id, &fw_info); + if (err) { + rsp->s_fw_info.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; + dev_err(&oct->pdev->dev, "Get VF info failed via host control Mbox\n"); + return; + } + + rsp->s_fw_info.pkind = fw_info.pkind; + rsp->s_fw_info.fsz = fw_info.fsz; + rsp->s_fw_info.rx_ol_flags = fw_info.rx_ol_flags; + rsp->s_fw_info.tx_ol_flags = fw_info.tx_ol_flags; + + rsp->s_fw_info.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; +} + +static void octep_pfvf_set_offloads(struct octep_device *oct, u32 vf_id, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + struct octep_ctrl_net_offloads offloads = { + .rx_offloads = cmd.s_offloads.rx_ol_flags, + .tx_offloads = cmd.s_offloads.tx_ol_flags + }; + int err; + + err = octep_ctrl_net_set_offloads(oct, vf_id, &offloads, true); + if (err) { + rsp->s_offloads.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; + dev_err(&oct->pdev->dev, "Set VF offloads failed via host control Mbox\n"); + return; + } + rsp->s_offloads.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; +} + +int octep_setup_pfvf_mbox(struct octep_device *oct) +{ + int i = 0, num_vfs = 0, rings_per_vf = 0; + int ring = 0; + + num_vfs = oct->conf->sriov_cfg.active_vfs; + rings_per_vf = oct->conf->sriov_cfg.max_rings_per_vf; + + for (i = 0; i < num_vfs; i++) { + ring = rings_per_vf * i; + oct->mbox[ring] = vzalloc(sizeof(*oct->mbox[ring])); + + if (!oct->mbox[ring]) + goto free_mbox; + + memset(oct->mbox[ring], 0, sizeof(struct octep_mbox)); + memset(&oct->vf_info[i], 0, sizeof(struct octep_pfvf_info)); + mutex_init(&oct->mbox[ring]->lock); + INIT_WORK(&oct->mbox[ring]->wk.work, octep_pfvf_mbox_work); + oct->mbox[ring]->wk.ctxptr = oct->mbox[ring]; + oct->mbox[ring]->oct = oct; + oct->mbox[ring]->vf_id = i; + oct->hw_ops.setup_mbox_regs(oct, ring); + } + return 0; + +free_mbox: + while (i) { + i--; + ring = rings_per_vf * i; + cancel_work_sync(&oct->mbox[ring]->wk.work); + mutex_destroy(&oct->mbox[ring]->lock); + vfree(oct->mbox[ring]); + oct->mbox[ring] = NULL; + } + return -ENOMEM; +} + +void octep_delete_pfvf_mbox(struct octep_device *oct) +{ + int rings_per_vf = oct->conf->sriov_cfg.max_rings_per_vf; + int num_vfs = oct->conf->sriov_cfg.active_vfs; + int i = 0, ring = 0, vf_srn = 0; + + for (i = 0; i < num_vfs; i++) { + ring = vf_srn + rings_per_vf * i; + if (!oct->mbox[ring]) + continue; + + if (work_pending(&oct->mbox[ring]->wk.work)) + cancel_work_sync(&oct->mbox[ring]->wk.work); + + mutex_destroy(&oct->mbox[ring]->lock); + vfree(oct->mbox[ring]); + oct->mbox[ring] = NULL; + } +} + +static void octep_pfvf_pf_get_data(struct octep_device *oct, + struct octep_mbox *mbox, int vf_id, + union octep_pfvf_mbox_word cmd, + union octep_pfvf_mbox_word *rsp) +{ + int length = 0; + int i = 0; + int err; + struct octep_iface_link_info link_info; + struct octep_iface_rx_stats rx_stats; + struct octep_iface_tx_stats tx_stats; + + rsp->s_data.type = OCTEP_PFVF_MBOX_TYPE_RSP_ACK; + + if (cmd.s_data.frag != OCTEP_PFVF_MBOX_MORE_FRAG_FLAG) { + mbox->config_data_index = 0; + memset(mbox->config_data, 0, MAX_VF_PF_MBOX_DATA_SIZE); + /* Based on the OPCODE CMD the PF driver + * specific API should be called to fetch + * the requested data + */ + switch (cmd.s.opcode) { + case OCTEP_PFVF_MBOX_CMD_GET_LINK_INFO: + memset(&link_info, 0, sizeof(link_info)); + err = octep_ctrl_net_get_link_info(oct, vf_id, &link_info); + if (!err) { + mbox->message_len = sizeof(link_info); + *((int32_t *)rsp->s_data.data) = mbox->message_len; + memcpy(mbox->config_data, (u8 *)&link_info, sizeof(link_info)); + } else { + rsp->s_data.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; + return; + } + break; + case OCTEP_PFVF_MBOX_CMD_GET_STATS: + memset(&rx_stats, 0, sizeof(rx_stats)); + memset(&tx_stats, 0, sizeof(tx_stats)); + err = octep_ctrl_net_get_if_stats(oct, vf_id, &rx_stats, &tx_stats); + if (!err) { + mbox->message_len = sizeof(rx_stats) + sizeof(tx_stats); + *((int32_t *)rsp->s_data.data) = mbox->message_len; + memcpy(mbox->config_data, (u8 *)&rx_stats, sizeof(rx_stats)); + memcpy(mbox->config_data + sizeof(rx_stats), (u8 *)&tx_stats, + sizeof(tx_stats)); + + } else { + rsp->s_data.type = OCTEP_PFVF_MBOX_TYPE_RSP_NACK; + return; + } + break; + } + *((int32_t *)rsp->s_data.data) = mbox->message_len; + return; + } + + if (mbox->message_len > OCTEP_PFVF_MBOX_MAX_DATA_SIZE) + length = OCTEP_PFVF_MBOX_MAX_DATA_SIZE; + else + length = mbox->message_len; + + mbox->message_len -= length; + + for (i = 0; i < length; i++) { + rsp->s_data.data[i] = + mbox->config_data[mbox->config_data_index]; + mbox->config_data_index++; + } +} + +void octep_pfvf_notify(struct octep_device *oct, struct octep_ctrl_mbox_msg *msg) +{ + union octep_pfvf_mbox_word notif = { 0 }; + struct octep_ctrl_net_f2h_req *req; + + req = (struct octep_ctrl_net_f2h_req *)msg->sg_list[0].msg; + switch (req->hdr.s.cmd) { + case OCTEP_CTRL_NET_F2H_CMD_LINK_STATUS: + notif.s_link_status.opcode = OCTEP_PFVF_MBOX_NOTIF_LINK_STATUS; + notif.s_link_status.status = req->link.state; + break; + default: + pr_info("Unknown mbox notif for vf: %u\n", + req->hdr.s.cmd); + return; + } + + notif.s.type = OCTEP_PFVF_MBOX_TYPE_CMD; + octep_send_notification(oct, msg->hdr.s.vf_idx, notif); +} + +void octep_pfvf_mbox_work(struct work_struct *work) +{ + struct octep_pfvf_mbox_wk *wk = container_of(work, struct octep_pfvf_mbox_wk, work); + union octep_pfvf_mbox_word cmd = { 0 }; + union octep_pfvf_mbox_word rsp = { 0 }; + struct octep_mbox *mbox = NULL; + struct octep_device *oct = NULL; + int vf_id; + + mbox = (struct octep_mbox *)wk->ctxptr; + oct = (struct octep_device *)mbox->oct; + vf_id = mbox->vf_id; + + mutex_lock(&mbox->lock); + cmd.u64 = readq(mbox->vf_pf_data_reg); + rsp.u64 = 0; + + switch (cmd.s.opcode) { + case OCTEP_PFVF_MBOX_CMD_VERSION: + octep_pfvf_validate_version(oct, vf_id, cmd, &rsp); + break; + case OCTEP_PFVF_MBOX_CMD_GET_LINK_STATUS: + octep_pfvf_get_link_status(oct, vf_id, cmd, &rsp); + break; + case OCTEP_PFVF_MBOX_CMD_SET_LINK_STATUS: + octep_pfvf_set_link_status(oct, vf_id, cmd, &rsp); + break; + case OCTEP_PFVF_MBOX_CMD_SET_RX_STATE: + octep_pfvf_set_rx_state(oct, vf_id, cmd, &rsp); + break; + case OCTEP_PFVF_MBOX_CMD_SET_MTU: + octep_pfvf_set_mtu(oct, vf_id, cmd, &rsp); + break; + case OCTEP_PFVF_MBOX_CMD_SET_MAC_ADDR: + octep_pfvf_set_mac_addr(oct, vf_id, cmd, &rsp); + break; + case OCTEP_PFVF_MBOX_CMD_GET_MAC_ADDR: + octep_pfvf_get_mac |