| // SPDX-License-Identifier: GPL-2.0-only |
| /* Copyright (C) 2020 Marvell. */ |
| |
| #include <linux/firmware.h> |
| #include "otx2_cpt_hw_types.h" |
| #include "otx2_cpt_common.h" |
| #include "otx2_cptpf_ucode.h" |
| #include "otx2_cptpf.h" |
| #include "cn10k_cpt.h" |
| #include "rvu_reg.h" |
| |
| #define OTX2_CPT_DRV_NAME "rvu_cptpf" |
| #define OTX2_CPT_DRV_STRING "Marvell RVU CPT Physical Function Driver" |
| |
| static void cptpf_enable_vfpf_mbox_intr(struct otx2_cptpf_dev *cptpf, |
| int num_vfs) |
| { |
| int ena_bits; |
| |
| /* Clear any pending interrupts */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFPF_MBOX_INTX(0), ~0x0ULL); |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFPF_MBOX_INTX(1), ~0x0ULL); |
| |
| /* Enable VF interrupts for VFs from 0 to 63 */ |
| ena_bits = ((num_vfs - 1) % 64); |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFPF_MBOX_INT_ENA_W1SX(0), |
| GENMASK_ULL(ena_bits, 0)); |
| |
| if (num_vfs > 64) { |
| /* Enable VF interrupts for VFs from 64 to 127 */ |
| ena_bits = num_vfs - 64 - 1; |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFPF_MBOX_INT_ENA_W1SX(1), |
| GENMASK_ULL(ena_bits, 0)); |
| } |
| } |
| |
| static void cptpf_disable_vfpf_mbox_intr(struct otx2_cptpf_dev *cptpf, |
| int num_vfs) |
| { |
| int vector; |
| |
| /* Disable VF-PF interrupts */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFPF_MBOX_INT_ENA_W1CX(0), ~0ULL); |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFPF_MBOX_INT_ENA_W1CX(1), ~0ULL); |
| /* Clear any pending interrupts */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFPF_MBOX_INTX(0), ~0ULL); |
| |
| vector = pci_irq_vector(cptpf->pdev, RVU_PF_INT_VEC_VFPF_MBOX0); |
| free_irq(vector, cptpf); |
| |
| if (num_vfs > 64) { |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFPF_MBOX_INTX(1), ~0ULL); |
| vector = pci_irq_vector(cptpf->pdev, RVU_PF_INT_VEC_VFPF_MBOX1); |
| free_irq(vector, cptpf); |
| } |
| } |
| |
| static void cptpf_enable_vf_flr_me_intrs(struct otx2_cptpf_dev *cptpf, |
| int num_vfs) |
| { |
| /* Clear FLR interrupt if any */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INTX(0), |
| INTR_MASK(num_vfs)); |
| |
| /* Enable VF FLR interrupts */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFFLR_INT_ENA_W1SX(0), INTR_MASK(num_vfs)); |
| /* Clear ME interrupt if any */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_VFME_INTX(0), |
| INTR_MASK(num_vfs)); |
| /* Enable VF ME interrupts */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFME_INT_ENA_W1SX(0), INTR_MASK(num_vfs)); |
| |
| if (num_vfs <= 64) |
| return; |
| |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_VFFLR_INTX(1), |
| INTR_MASK(num_vfs - 64)); |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFFLR_INT_ENA_W1SX(1), INTR_MASK(num_vfs - 64)); |
| |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_VFME_INTX(1), |
| INTR_MASK(num_vfs - 64)); |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFME_INT_ENA_W1SX(1), INTR_MASK(num_vfs - 64)); |
| } |
| |
| static void cptpf_disable_vf_flr_me_intrs(struct otx2_cptpf_dev *cptpf, |
| int num_vfs) |
| { |
| int vector; |
| |
| /* Disable VF FLR interrupts */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFFLR_INT_ENA_W1CX(0), INTR_MASK(num_vfs)); |
| vector = pci_irq_vector(cptpf->pdev, RVU_PF_INT_VEC_VFFLR0); |
| free_irq(vector, cptpf); |
| |
| /* Disable VF ME interrupts */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFME_INT_ENA_W1CX(0), INTR_MASK(num_vfs)); |
| vector = pci_irq_vector(cptpf->pdev, RVU_PF_INT_VEC_VFME0); |
| free_irq(vector, cptpf); |
| |
| if (num_vfs <= 64) |
| return; |
| |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFFLR_INT_ENA_W1CX(1), INTR_MASK(num_vfs - 64)); |
| vector = pci_irq_vector(cptpf->pdev, RVU_PF_INT_VEC_VFFLR1); |
| free_irq(vector, cptpf); |
| |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFME_INT_ENA_W1CX(1), INTR_MASK(num_vfs - 64)); |
| vector = pci_irq_vector(cptpf->pdev, RVU_PF_INT_VEC_VFME1); |
| free_irq(vector, cptpf); |
| } |
| |
| static void cptpf_flr_wq_handler(struct work_struct *work) |
| { |
| struct cptpf_flr_work *flr_work; |
| struct otx2_cptpf_dev *pf; |
| struct mbox_msghdr *req; |
| struct otx2_mbox *mbox; |
| int vf, reg = 0; |
| |
| flr_work = container_of(work, struct cptpf_flr_work, work); |
| pf = flr_work->pf; |
| mbox = &pf->afpf_mbox; |
| |
| vf = flr_work - pf->flr_work; |
| |
| req = otx2_mbox_alloc_msg_rsp(mbox, 0, sizeof(*req), |
| sizeof(struct msg_rsp)); |
| if (!req) |
| return; |
| |
| req->sig = OTX2_MBOX_REQ_SIG; |
| req->id = MBOX_MSG_VF_FLR; |
| req->pcifunc &= RVU_PFVF_FUNC_MASK; |
| req->pcifunc |= (vf + 1) & RVU_PFVF_FUNC_MASK; |
| |
| otx2_cpt_send_mbox_msg(mbox, pf->pdev); |
| |
| if (vf >= 64) { |
| reg = 1; |
| vf = vf - 64; |
| } |
| /* Clear transaction pending register */ |
| otx2_cpt_write64(pf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFTRPENDX(reg), BIT_ULL(vf)); |
| otx2_cpt_write64(pf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFFLR_INT_ENA_W1SX(reg), BIT_ULL(vf)); |
| } |
| |
| static irqreturn_t cptpf_vf_flr_intr(int __always_unused irq, void *arg) |
| { |
| int reg, dev, vf, start_vf, num_reg = 1; |
| struct otx2_cptpf_dev *cptpf = arg; |
| u64 intr; |
| |
| if (cptpf->max_vfs > 64) |
| num_reg = 2; |
| |
| for (reg = 0; reg < num_reg; reg++) { |
| intr = otx2_cpt_read64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFFLR_INTX(reg)); |
| if (!intr) |
| continue; |
| start_vf = 64 * reg; |
| for (vf = 0; vf < 64; vf++) { |
| if (!(intr & BIT_ULL(vf))) |
| continue; |
| dev = vf + start_vf; |
| queue_work(cptpf->flr_wq, &cptpf->flr_work[dev].work); |
| /* Clear interrupt */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFFLR_INTX(reg), BIT_ULL(vf)); |
| /* Disable the interrupt */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFFLR_INT_ENA_W1CX(reg), |
| BIT_ULL(vf)); |
| } |
| } |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t cptpf_vf_me_intr(int __always_unused irq, void *arg) |
| { |
| struct otx2_cptpf_dev *cptpf = arg; |
| int reg, vf, num_reg = 1; |
| u64 intr; |
| |
| if (cptpf->max_vfs > 64) |
| num_reg = 2; |
| |
| for (reg = 0; reg < num_reg; reg++) { |
| intr = otx2_cpt_read64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFME_INTX(reg)); |
| if (!intr) |
| continue; |
| for (vf = 0; vf < 64; vf++) { |
| if (!(intr & BIT_ULL(vf))) |
| continue; |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFTRPENDX(reg), BIT_ULL(vf)); |
| /* Clear interrupt */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_VFME_INTX(reg), BIT_ULL(vf)); |
| } |
| } |
| return IRQ_HANDLED; |
| } |
| |
| static void cptpf_unregister_vfpf_intr(struct otx2_cptpf_dev *cptpf, |
| int num_vfs) |
| { |
| cptpf_disable_vfpf_mbox_intr(cptpf, num_vfs); |
| cptpf_disable_vf_flr_me_intrs(cptpf, num_vfs); |
| } |
| |
| static int cptpf_register_vfpf_intr(struct otx2_cptpf_dev *cptpf, int num_vfs) |
| { |
| struct pci_dev *pdev = cptpf->pdev; |
| struct device *dev = &pdev->dev; |
| int ret, vector; |
| |
| vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFPF_MBOX0); |
| /* Register VF-PF mailbox interrupt handler */ |
| ret = request_irq(vector, otx2_cptpf_vfpf_mbox_intr, 0, "CPTVFPF Mbox0", |
| cptpf); |
| if (ret) { |
| dev_err(dev, |
| "IRQ registration failed for PFVF mbox0 irq\n"); |
| return ret; |
| } |
| vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFFLR0); |
| /* Register VF FLR interrupt handler */ |
| ret = request_irq(vector, cptpf_vf_flr_intr, 0, "CPTPF FLR0", cptpf); |
| if (ret) { |
| dev_err(dev, |
| "IRQ registration failed for VFFLR0 irq\n"); |
| goto free_mbox0_irq; |
| } |
| vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFME0); |
| /* Register VF ME interrupt handler */ |
| ret = request_irq(vector, cptpf_vf_me_intr, 0, "CPTPF ME0", cptpf); |
| if (ret) { |
| dev_err(dev, |
| "IRQ registration failed for PFVF mbox0 irq\n"); |
| goto free_flr0_irq; |
| } |
| |
| if (num_vfs > 64) { |
| vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFPF_MBOX1); |
| ret = request_irq(vector, otx2_cptpf_vfpf_mbox_intr, 0, |
| "CPTVFPF Mbox1", cptpf); |
| if (ret) { |
| dev_err(dev, |
| "IRQ registration failed for PFVF mbox1 irq\n"); |
| goto free_me0_irq; |
| } |
| vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFFLR1); |
| /* Register VF FLR interrupt handler */ |
| ret = request_irq(vector, cptpf_vf_flr_intr, 0, "CPTPF FLR1", |
| cptpf); |
| if (ret) { |
| dev_err(dev, |
| "IRQ registration failed for VFFLR1 irq\n"); |
| goto free_mbox1_irq; |
| } |
| vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFME1); |
| /* Register VF FLR interrupt handler */ |
| ret = request_irq(vector, cptpf_vf_me_intr, 0, "CPTPF ME1", |
| cptpf); |
| if (ret) { |
| dev_err(dev, |
| "IRQ registration failed for VFFLR1 irq\n"); |
| goto free_flr1_irq; |
| } |
| } |
| cptpf_enable_vfpf_mbox_intr(cptpf, num_vfs); |
| cptpf_enable_vf_flr_me_intrs(cptpf, num_vfs); |
| |
| return 0; |
| |
| free_flr1_irq: |
| vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFFLR1); |
| free_irq(vector, cptpf); |
| free_mbox1_irq: |
| vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFPF_MBOX1); |
| free_irq(vector, cptpf); |
| free_me0_irq: |
| vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFME0); |
| free_irq(vector, cptpf); |
| free_flr0_irq: |
| vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFFLR0); |
| free_irq(vector, cptpf); |
| free_mbox0_irq: |
| vector = pci_irq_vector(pdev, RVU_PF_INT_VEC_VFPF_MBOX0); |
| free_irq(vector, cptpf); |
| return ret; |
| } |
| |
| static void cptpf_flr_wq_destroy(struct otx2_cptpf_dev *pf) |
| { |
| if (!pf->flr_wq) |
| return; |
| destroy_workqueue(pf->flr_wq); |
| pf->flr_wq = NULL; |
| kfree(pf->flr_work); |
| } |
| |
| static int cptpf_flr_wq_init(struct otx2_cptpf_dev *cptpf, int num_vfs) |
| { |
| int vf; |
| |
| cptpf->flr_wq = alloc_ordered_workqueue("cptpf_flr_wq", 0); |
| if (!cptpf->flr_wq) |
| return -ENOMEM; |
| |
| cptpf->flr_work = kcalloc(num_vfs, sizeof(struct cptpf_flr_work), |
| GFP_KERNEL); |
| if (!cptpf->flr_work) |
| goto destroy_wq; |
| |
| for (vf = 0; vf < num_vfs; vf++) { |
| cptpf->flr_work[vf].pf = cptpf; |
| INIT_WORK(&cptpf->flr_work[vf].work, cptpf_flr_wq_handler); |
| } |
| return 0; |
| |
| destroy_wq: |
| destroy_workqueue(cptpf->flr_wq); |
| return -ENOMEM; |
| } |
| |
| static int cptpf_vfpf_mbox_init(struct otx2_cptpf_dev *cptpf, int num_vfs) |
| { |
| struct device *dev = &cptpf->pdev->dev; |
| u64 vfpf_mbox_base; |
| int err, i; |
| |
| cptpf->vfpf_mbox_wq = alloc_workqueue("cpt_vfpf_mailbox", |
| WQ_UNBOUND | WQ_HIGHPRI | |
| WQ_MEM_RECLAIM, 1); |
| if (!cptpf->vfpf_mbox_wq) |
| return -ENOMEM; |
| |
| /* Map VF-PF mailbox memory */ |
| if (test_bit(CN10K_MBOX, &cptpf->cap_flag)) |
| vfpf_mbox_base = readq(cptpf->reg_base + RVU_PF_VF_MBOX_ADDR); |
| else |
| vfpf_mbox_base = readq(cptpf->reg_base + RVU_PF_VF_BAR4_ADDR); |
| |
| if (!vfpf_mbox_base) { |
| dev_err(dev, "VF-PF mailbox address not configured\n"); |
| err = -ENOMEM; |
| goto free_wqe; |
| } |
| cptpf->vfpf_mbox_base = devm_ioremap_wc(dev, vfpf_mbox_base, |
| MBOX_SIZE * cptpf->max_vfs); |
| if (!cptpf->vfpf_mbox_base) { |
| dev_err(dev, "Mapping of VF-PF mailbox address failed\n"); |
| err = -ENOMEM; |
| goto free_wqe; |
| } |
| err = otx2_mbox_init(&cptpf->vfpf_mbox, cptpf->vfpf_mbox_base, |
| cptpf->pdev, cptpf->reg_base, MBOX_DIR_PFVF, |
| num_vfs); |
| if (err) |
| goto free_wqe; |
| |
| for (i = 0; i < num_vfs; i++) { |
| cptpf->vf[i].vf_id = i; |
| cptpf->vf[i].cptpf = cptpf; |
| cptpf->vf[i].intr_idx = i % 64; |
| INIT_WORK(&cptpf->vf[i].vfpf_mbox_work, |
| otx2_cptpf_vfpf_mbox_handler); |
| } |
| return 0; |
| |
| free_wqe: |
| destroy_workqueue(cptpf->vfpf_mbox_wq); |
| return err; |
| } |
| |
| static void cptpf_vfpf_mbox_destroy(struct otx2_cptpf_dev *cptpf) |
| { |
| destroy_workqueue(cptpf->vfpf_mbox_wq); |
| otx2_mbox_destroy(&cptpf->vfpf_mbox); |
| } |
| |
| static void cptpf_disable_afpf_mbox_intr(struct otx2_cptpf_dev *cptpf) |
| { |
| /* Disable AF-PF interrupt */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_INT_ENA_W1C, |
| 0x1ULL); |
| /* Clear interrupt if any */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_INT, 0x1ULL); |
| } |
| |
| static int cptpf_register_afpf_mbox_intr(struct otx2_cptpf_dev *cptpf) |
| { |
| struct pci_dev *pdev = cptpf->pdev; |
| struct device *dev = &pdev->dev; |
| int ret, irq; |
| |
| irq = pci_irq_vector(pdev, RVU_PF_INT_VEC_AFPF_MBOX); |
| /* Register AF-PF mailbox interrupt handler */ |
| ret = devm_request_irq(dev, irq, otx2_cptpf_afpf_mbox_intr, 0, |
| "CPTAFPF Mbox", cptpf); |
| if (ret) { |
| dev_err(dev, |
| "IRQ registration failed for PFAF mbox irq\n"); |
| return ret; |
| } |
| /* Clear interrupt if any, to avoid spurious interrupts */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_INT, 0x1ULL); |
| /* Enable AF-PF interrupt */ |
| otx2_cpt_write64(cptpf->reg_base, BLKADDR_RVUM, 0, RVU_PF_INT_ENA_W1S, |
| 0x1ULL); |
| |
| ret = otx2_cpt_send_ready_msg(&cptpf->afpf_mbox, cptpf->pdev); |
| if (ret) { |
| dev_warn(dev, |
| "AF not responding to mailbox, deferring probe\n"); |
| cptpf_disable_afpf_mbox_intr(cptpf); |
| return -EPROBE_DEFER; |
| } |
| return 0; |
| } |
| |
| static int cptpf_afpf_mbox_init(struct otx2_cptpf_dev *cptpf) |
| { |
| struct pci_dev *pdev = cptpf->pdev; |
| resource_size_t offset; |
| int err; |
| |
| cptpf->afpf_mbox_wq = alloc_workqueue("cpt_afpf_mailbox", |
| WQ_UNBOUND | WQ_HIGHPRI | |
| WQ_MEM_RECLAIM, 1); |
| if (!cptpf->afpf_mbox_wq) |
| return -ENOMEM; |
| |
| offset = pci_resource_start(pdev, PCI_MBOX_BAR_NUM); |
| /* Map AF-PF mailbox memory */ |
| cptpf->afpf_mbox_base = devm_ioremap_wc(&pdev->dev, offset, MBOX_SIZE); |
| if (!cptpf->afpf_mbox_base) { |
| dev_err(&pdev->dev, "Unable to map BAR4\n"); |
| err = -ENOMEM; |
| goto error; |
| } |
| |
| err = otx2_mbox_init(&cptpf->afpf_mbox, cptpf->afpf_mbox_base, |
| pdev, cptpf->reg_base, MBOX_DIR_PFAF, 1); |
| if (err) |
| goto error; |
| |
| INIT_WORK(&cptpf->afpf_mbox_work, otx2_cptpf_afpf_mbox_handler); |
| return 0; |
| |
| error: |
| destroy_workqueue(cptpf->afpf_mbox_wq); |
| return err; |
| } |
| |
| static void cptpf_afpf_mbox_destroy(struct otx2_cptpf_dev *cptpf) |
| { |
| destroy_workqueue(cptpf->afpf_mbox_wq); |
| otx2_mbox_destroy(&cptpf->afpf_mbox); |
| } |
| |
| static ssize_t kvf_limits_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct otx2_cptpf_dev *cptpf = dev_get_drvdata(dev); |
| |
| return sprintf(buf, "%d\n", cptpf->kvf_limits); |
| } |
| |
| static ssize_t kvf_limits_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct otx2_cptpf_dev *cptpf = dev_get_drvdata(dev); |
| int lfs_num; |
| |
| if (kstrtoint(buf, 0, &lfs_num)) { |
| dev_err(dev, "lfs count %d must be in range [1 - %d]\n", |
| lfs_num, num_online_cpus()); |
| return -EINVAL; |
| } |
| if (lfs_num < 1 || lfs_num > num_online_cpus()) { |
| dev_err(dev, "lfs count %d must be in range [1 - %d]\n", |
| lfs_num, num_online_cpus()); |
| return -EINVAL; |
| } |
| cptpf->kvf_limits = lfs_num; |
| |
| return count; |
| } |
| |
| static DEVICE_ATTR_RW(kvf_limits); |
| static struct attribute *cptpf_attrs[] = { |
| &dev_attr_kvf_limits.attr, |
| NULL |
| }; |
| |
| static const struct attribute_group cptpf_sysfs_group = { |
| .attrs = cptpf_attrs, |
| }; |
| |
| static int cpt_is_pf_usable(struct otx2_cptpf_dev *cptpf) |
| { |
| u64 rev; |
| |
| rev = otx2_cpt_read64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_BLOCK_ADDRX_DISC(BLKADDR_RVUM)); |
| rev = (rev >> 12) & 0xFF; |
| /* |
| * Check if AF has setup revision for RVUM block, otherwise |
| * driver probe should be deferred until AF driver comes up |
| */ |
| if (!rev) { |
| dev_warn(&cptpf->pdev->dev, |
| "AF is not initialized, deferring probe\n"); |
| return -EPROBE_DEFER; |
| } |
| return 0; |
| } |
| |
| static int cptx_device_reset(struct otx2_cptpf_dev *cptpf, int blkaddr) |
| { |
| int timeout = 10, ret; |
| u64 reg = 0; |
| |
| ret = otx2_cpt_write_af_reg(&cptpf->afpf_mbox, cptpf->pdev, |
| CPT_AF_BLK_RST, 0x1, blkaddr); |
| if (ret) |
| return ret; |
| |
| do { |
| ret = otx2_cpt_read_af_reg(&cptpf->afpf_mbox, cptpf->pdev, |
| CPT_AF_BLK_RST, ®, blkaddr); |
| if (ret) |
| return ret; |
| |
| if (!((reg >> 63) & 0x1)) |
| break; |
| |
| usleep_range(10000, 20000); |
| if (timeout-- < 0) |
| return -EBUSY; |
| } while (1); |
| |
| return ret; |
| } |
| |
| static int cptpf_device_reset(struct otx2_cptpf_dev *cptpf) |
| { |
| int ret = 0; |
| |
| if (cptpf->has_cpt1) { |
| ret = cptx_device_reset(cptpf, BLKADDR_CPT1); |
| if (ret) |
| return ret; |
| } |
| return cptx_device_reset(cptpf, BLKADDR_CPT0); |
| } |
| |
| static void cptpf_check_block_implemented(struct otx2_cptpf_dev *cptpf) |
| { |
| u64 cfg; |
| |
| cfg = otx2_cpt_read64(cptpf->reg_base, BLKADDR_RVUM, 0, |
| RVU_PF_BLOCK_ADDRX_DISC(BLKADDR_CPT1)); |
| if (cfg & BIT_ULL(11)) |
| cptpf->has_cpt1 = true; |
| } |
| |
| static int cptpf_device_init(struct otx2_cptpf_dev *cptpf) |
| { |
| union otx2_cptx_af_constants1 af_cnsts1 = {0}; |
| int ret = 0; |
| |
| /* check if 'implemented' bit is set for block BLKADDR_CPT1 */ |
| cptpf_check_block_implemented(cptpf); |
| /* Reset the CPT PF device */ |
| ret = cptpf_device_reset(cptpf); |
| if (ret) |
| return ret; |
| |
| /* Get number of SE, IE and AE engines */ |
| ret = otx2_cpt_read_af_reg(&cptpf->afpf_mbox, cptpf->pdev, |
| CPT_AF_CONSTANTS1, &af_cnsts1.u, |
| BLKADDR_CPT0); |
| if (ret) |
| return ret; |
| |
| cptpf->eng_grps.avail.max_se_cnt = af_cnsts1.s.se; |
| cptpf->eng_grps.avail.max_ie_cnt = af_cnsts1.s.ie; |
| cptpf->eng_grps.avail.max_ae_cnt = af_cnsts1.s.ae; |
| |
| /* Disable all cores */ |
| ret = otx2_cpt_disable_all_cores(cptpf); |
| |
| return ret; |
| } |
| |
| static int cptpf_sriov_disable(struct pci_dev *pdev) |
| { |
| struct otx2_cptpf_dev *cptpf = pci_get_drvdata(pdev); |
| int num_vfs = pci_num_vf(pdev); |
| |
| if (!num_vfs) |
| return 0; |
| |
| pci_disable_sriov(pdev); |
| cptpf_unregister_vfpf_intr(cptpf, num_vfs); |
| cptpf_flr_wq_destroy(cptpf); |
| cptpf_vfpf_mbox_destroy(cptpf); |
| module_put(THIS_MODULE); |
| cptpf->enabled_vfs = 0; |
| |
| return 0; |
| } |
| |
| static int cptpf_sriov_enable(struct pci_dev *pdev, int num_vfs) |
| { |
| struct otx2_cptpf_dev *cptpf = pci_get_drvdata(pdev); |
| int ret; |
| |
| /* Initialize VF<=>PF mailbox */ |
| ret = cptpf_vfpf_mbox_init(cptpf, num_vfs); |
| if (ret) |
| return ret; |
| |
| ret = cptpf_flr_wq_init(cptpf, num_vfs); |
| if (ret) |
| goto destroy_mbox; |
| /* Register VF<=>PF mailbox interrupt */ |
| ret = cptpf_register_vfpf_intr(cptpf, num_vfs); |
| if (ret) |
| goto destroy_flr; |
| |
| /* Get CPT HW capabilities using LOAD_FVC operation. */ |
| ret = otx2_cpt_discover_eng_capabilities(cptpf); |
| if (ret) |
| goto disable_intr; |
| |
| ret = otx2_cpt_create_eng_grps(cptpf, &cptpf->eng_grps); |
| if (ret) |
| goto disable_intr; |
| |
| cptpf->enabled_vfs = num_vfs; |
| ret = pci_enable_sriov(pdev, num_vfs); |
| if (ret) |
| goto disable_intr; |
| |
| dev_notice(&cptpf->pdev->dev, "VFs enabled: %d\n", num_vfs); |
| |
| try_module_get(THIS_MODULE); |
| return num_vfs; |
| |
| disable_intr: |
| cptpf_unregister_vfpf_intr(cptpf, num_vfs); |
| cptpf->enabled_vfs = 0; |
| destroy_flr: |
| cptpf_flr_wq_destroy(cptpf); |
| destroy_mbox: |
| cptpf_vfpf_mbox_destroy(cptpf); |
| return ret; |
| } |
| |
| static int otx2_cptpf_sriov_configure(struct pci_dev *pdev, int num_vfs) |
| { |
| if (num_vfs > 0) { |
| return cptpf_sriov_enable(pdev, num_vfs); |
| } else { |
| return cptpf_sriov_disable(pdev); |
| } |
| } |
| |
| static int otx2_cptpf_probe(struct pci_dev *pdev, |
| const struct pci_device_id *ent) |
| { |
| struct device *dev = &pdev->dev; |
| struct otx2_cptpf_dev *cptpf; |
| int err; |
| |
| cptpf = devm_kzalloc(dev, sizeof(*cptpf), GFP_KERNEL); |
| if (!cptpf) |
| return -ENOMEM; |
| |
| err = pcim_enable_device(pdev); |
| if (err) { |
| dev_err(dev, "Failed to enable PCI device\n"); |
| goto clear_drvdata; |
| } |
| |
| err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); |
| if (err) { |
| dev_err(dev, "Unable to get usable DMA configuration\n"); |
| goto clear_drvdata; |
| } |
| /* Map PF's configuration registers */ |
| err = pcim_iomap_regions_request_all(pdev, 1 << PCI_PF_REG_BAR_NUM, |
| OTX2_CPT_DRV_NAME); |
| if (err) { |
| dev_err(dev, "Couldn't get PCI resources 0x%x\n", err); |
| goto clear_drvdata; |
| } |
| pci_set_master(pdev); |
| pci_set_drvdata(pdev, cptpf); |
| cptpf->pdev = pdev; |
| |
| cptpf->reg_base = pcim_iomap_table(pdev)[PCI_PF_REG_BAR_NUM]; |
| |
| /* Check if AF driver is up, otherwise defer probe */ |
| err = cpt_is_pf_usable(cptpf); |
| if (err) |
| goto clear_drvdata; |
| |
| err = pci_alloc_irq_vectors(pdev, RVU_PF_INT_VEC_CNT, |
| RVU_PF_INT_VEC_CNT, PCI_IRQ_MSIX); |
| if (err < 0) { |
| dev_err(dev, "Request for %d msix vectors failed\n", |
| RVU_PF_INT_VEC_CNT); |
| goto clear_drvdata; |
| } |
| otx2_cpt_set_hw_caps(pdev, &cptpf->cap_flag); |
| /* Initialize AF-PF mailbox */ |
| err = cptpf_afpf_mbox_init(cptpf); |
| if (err) |
| goto clear_drvdata; |
| /* Register mailbox interrupt */ |
| err = cptpf_register_afpf_mbox_intr(cptpf); |
| if (err) |
| goto destroy_afpf_mbox; |
| |
| cptpf->max_vfs = pci_sriov_get_totalvfs(pdev); |
| |
| err = cn10k_cptpf_lmtst_init(cptpf); |
| if (err) |
| goto unregister_intr; |
| |
| /* Initialize CPT PF device */ |
| err = cptpf_device_init(cptpf); |
| if (err) |
| goto unregister_intr; |
| |
| /* Initialize engine groups */ |
| err = otx2_cpt_init_eng_grps(pdev, &cptpf->eng_grps); |
| if (err) |
| goto unregister_intr; |
| |
| err = sysfs_create_group(&dev->kobj, &cptpf_sysfs_group); |
| if (err) |
| goto cleanup_eng_grps; |
| return 0; |
| |
| cleanup_eng_grps: |
| otx2_cpt_cleanup_eng_grps(pdev, &cptpf->eng_grps); |
| unregister_intr: |
| cptpf_disable_afpf_mbox_intr(cptpf); |
| destroy_afpf_mbox: |
| cptpf_afpf_mbox_destroy(cptpf); |
| clear_drvdata: |
| pci_set_drvdata(pdev, NULL); |
| return err; |
| } |
| |
| static void otx2_cptpf_remove(struct pci_dev *pdev) |
| { |
| struct otx2_cptpf_dev *cptpf = pci_get_drvdata(pdev); |
| |
| if (!cptpf) |
| return; |
| |
| cptpf_sriov_disable(pdev); |
| /* Delete sysfs entry created for kernel VF limits */ |
| sysfs_remove_group(&pdev->dev.kobj, &cptpf_sysfs_group); |
| /* Cleanup engine groups */ |
| otx2_cpt_cleanup_eng_grps(pdev, &cptpf->eng_grps); |
| /* Disable AF-PF mailbox interrupt */ |
| cptpf_disable_afpf_mbox_intr(cptpf); |
| /* Destroy AF-PF mbox */ |
| cptpf_afpf_mbox_destroy(cptpf); |
| pci_set_drvdata(pdev, NULL); |
| } |
| |
| /* Supported devices */ |
| static const struct pci_device_id otx2_cpt_id_table[] = { |
| { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OTX2_CPT_PCI_PF_DEVICE_ID) }, |
| { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, CN10K_CPT_PCI_PF_DEVICE_ID) }, |
| { 0, } /* end of table */ |
| }; |
| |
| static struct pci_driver otx2_cpt_pci_driver = { |
| .name = OTX2_CPT_DRV_NAME, |
| .id_table = otx2_cpt_id_table, |
| .probe = otx2_cptpf_probe, |
| .remove = otx2_cptpf_remove, |
| .sriov_configure = otx2_cptpf_sriov_configure |
| }; |
| |
| module_pci_driver(otx2_cpt_pci_driver); |
| |
| MODULE_AUTHOR("Marvell"); |
| MODULE_DESCRIPTION(OTX2_CPT_DRV_STRING); |
| MODULE_LICENSE("GPL v2"); |
| MODULE_DEVICE_TABLE(pci, otx2_cpt_id_table); |