From: Vamsi Attunuru <[email protected]> Handle vDPA device add and remove events from Octeon firmware. Use irq 0 for event delivery as device interrupts are multiplexed.
Signed-off-by: Vamsi Attunuru <[email protected]> --- drivers/vdpa/octeon_ep/octep_vdpa.h | 22 ++++- drivers/vdpa/octeon_ep/octep_vdpa_main.c | 107 ++++++++++++++++++++--- 2 files changed, 115 insertions(+), 14 deletions(-) diff --git a/drivers/vdpa/octeon_ep/octep_vdpa.h b/drivers/vdpa/octeon_ep/octep_vdpa.h index 53b020b019f7..a67bf50e4075 100644 --- a/drivers/vdpa/octeon_ep/octep_vdpa.h +++ b/drivers/vdpa/octeon_ep/octep_vdpa.h @@ -30,8 +30,10 @@ #define OCTEP_EPF_RINFO(x) (0x000209f0 | ((x) << 25)) #define OCTEP_VF_MBOX_DATA(x) (0x00010210 | ((x) << 17)) #define OCTEP_PF_MBOX_DATA(x) (0x00022000 | ((x) << 4)) +#define OCTEP_VF_EVENT_STATE(x) (0x00010030 | ((x) << 17)) +#define OCTEP_VF_EVENT_REG(x) (0x00010060 | ((x) << 17)) #define OCTEP_VF_IN_CTRL(x) (0x00010000 | ((x) << 17)) -#define OCTEP_VF_IN_CTRL_RPVF(val) (((val) >> 48) & 0xF) +#define OCTEP_VF_IN_CTRL_RPVF(val) (FIELD_GET(GENMASK_ULL(51, 48), val)) #define OCTEP_FW_READY_SIGNATURE0 0xFEEDFEED #define OCTEP_FW_READY_SIGNATURE1 0x3355ffaa @@ -43,9 +45,26 @@ enum octep_vdpa_dev_status { OCTEP_VDPA_DEV_STATUS_WAIT_FOR_BAR_INIT, OCTEP_VDPA_DEV_STATUS_INIT, OCTEP_VDPA_DEV_STATUS_READY, + OCTEP_VDPA_DEV_STATUS_ADDED, + OCTEP_VDPA_DEV_STATUS_REMOVED, OCTEP_VDPA_DEV_STATUS_UNINIT }; +enum octep_vdpa_dev_event_state { + OCTEP_VDPA_DEV_NO_EVENT, + OCTEP_VDPA_DEV_NEW_EVENT, + OCTEP_VDPA_DEV_EVENT_ACTIVE, + OCTEP_VDPA_DEV_EVENT_DONE, +}; + +enum octep_vdpa_dev_event { + OCTEP_VDPA_DEV_EVENT_NONE, + OCTEP_VDPA_DEV_EVENT_ACK, + OCTEP_VDPA_DEV_EVENT_NACK, + OCTEP_VDPA_DEV_ADD_EVENT, + OCTEP_VDPA_DEV_DEL_EVENT, +}; + struct octep_vring_info { struct vdpa_callback cb; void __iomem *notify_addr; @@ -86,6 +105,7 @@ struct octep_hw { u64 features; u16 nr_vring; u32 config_size; + int requested_irqs; int nb_irqs; int *irqs; u8 dev_id; diff --git a/drivers/vdpa/octeon_ep/octep_vdpa_main.c b/drivers/vdpa/octeon_ep/octep_vdpa_main.c index deaa8dc7813e..5b9d76632376 100644 --- a/drivers/vdpa/octeon_ep/octep_vdpa_main.c +++ b/drivers/vdpa/octeon_ep/octep_vdpa_main.c @@ -9,6 +9,7 @@ #include "octep_vdpa.h" #define OCTEP_VDPA_DRIVER_NAME "octep_vdpa" +#define OCTEP_VDPA_NAME_BUFSIZE 16 struct octep_pf { u8 __iomem *base[PCI_STD_NUM_BARS]; @@ -20,6 +21,11 @@ struct octep_pf { u16 vf_devid; }; +struct octep_vdpa_event_wk { + struct work_struct work; + void *ctxptr; +}; + struct octep_vdpa { struct vdpa_device vdpa; struct octep_hw *oct_hw; @@ -34,6 +40,8 @@ struct octep_vdpa_mgmt_dev { struct work_struct setup_task; /* Device status */ atomic_t status; + struct octep_vdpa *oct_vdpa; + struct octep_vdpa_event_wk event_wk; }; static struct octep_hw *vdpa_to_octep_hw(struct vdpa_device *vdpa_dev) @@ -45,6 +53,27 @@ static struct octep_hw *vdpa_to_octep_hw(struct vdpa_device *vdpa_dev) return oct_vdpa->oct_hw; } +static inline void octep_vdpa_dev_event_schedule(struct octep_hw *oct_hw) +{ + u8 __iomem *addr = oct_hw->base[OCTEP_HW_MBOX_BAR]; + struct octep_vdpa_mgmt_dev *mgmt_dev; + + mgmt_dev = container_of(oct_hw, struct octep_vdpa_mgmt_dev, oct_hw); + writeb(OCTEP_VDPA_DEV_EVENT_ACTIVE, addr + OCTEP_VF_EVENT_STATE(0)); + schedule_work(&mgmt_dev->event_wk.work); +} + +static irqreturn_t octep_vdpa_dev_event_handler(int irq, void *data) +{ + struct octep_hw *oct_hw = data; + + if (readb(oct_hw->base[OCTEP_HW_MBOX_BAR] + OCTEP_VF_EVENT_STATE(0)) == + OCTEP_VDPA_DEV_NEW_EVENT) + octep_vdpa_dev_event_schedule(oct_hw); + + return IRQ_HANDLED; +} + static irqreturn_t octep_vdpa_intr_handler(int irq, void *data) { struct octep_hw *oct_hw = data; @@ -73,11 +102,14 @@ static irqreturn_t octep_vdpa_intr_handler(int irq, void *data) } /* Check for config interrupt. Config uses the first interrupt */ - if (unlikely(irq == oct_hw->irqs[0] && ioread8(oct_hw->isr))) { - iowrite8(0, oct_hw->isr); + if (unlikely(irq == oct_hw->irqs[0])) { + if (ioread8(oct_hw->isr)) { + iowrite8(0, oct_hw->isr); - if (oct_hw->config_cb.callback) - oct_hw->config_cb.callback(oct_hw->config_cb.private); + if (oct_hw->config_cb.callback) + oct_hw->config_cb.callback(oct_hw->config_cb.private); + } + octep_vdpa_dev_event_handler(irq, data); } return IRQ_HANDLED; @@ -101,33 +133,41 @@ static void octep_free_irqs(struct octep_hw *oct_hw) pci_free_irq_vectors(pdev); devm_kfree(&pdev->dev, oct_hw->irqs); oct_hw->irqs = NULL; + oct_hw->requested_irqs = 0; } -static int octep_request_irqs(struct octep_hw *oct_hw) +static int octep_request_irqs(struct octep_hw *oct_hw, irqreturn_t (*irq_handler)(int, void *), + int nb_irqs) { struct pci_dev *pdev = oct_hw->pdev; int ret, irq, idx; - oct_hw->irqs = devm_kcalloc(&pdev->dev, oct_hw->nb_irqs, sizeof(int), GFP_KERNEL); + if ((oct_hw->requested_irqs != nb_irqs) || (nb_irqs == 1)) + octep_free_irqs(oct_hw); + else + return 0; + + oct_hw->irqs = devm_kcalloc(&pdev->dev, nb_irqs, sizeof(int), GFP_KERNEL); if (!oct_hw->irqs) return -ENOMEM; - ret = pci_alloc_irq_vectors(pdev, 1, oct_hw->nb_irqs, PCI_IRQ_MSIX); + ret = pci_alloc_irq_vectors(pdev, 1, nb_irqs, PCI_IRQ_MSIX); if (ret < 0) { dev_err(&pdev->dev, "Failed to alloc msix vector"); return ret; } - for (idx = 0; idx < oct_hw->nb_irqs; idx++) { + for (idx = 0; idx < nb_irqs; idx++) { irq = pci_irq_vector(pdev, idx); - ret = devm_request_irq(&pdev->dev, irq, octep_vdpa_intr_handler, 0, - dev_name(&pdev->dev), oct_hw); + ret = devm_request_irq(&pdev->dev, irq, irq_handler, 0, dev_name(&pdev->dev), + oct_hw); if (ret) { dev_err(&pdev->dev, "Failed to register interrupt handler\n"); goto free_irqs; } oct_hw->irqs[idx] = irq; } + oct_hw->requested_irqs = nb_irqs; return 0; @@ -189,7 +229,7 @@ static void octep_vdpa_set_status(struct vdpa_device *vdpa_dev, u8 status) if ((status & VIRTIO_CONFIG_S_DRIVER_OK) && !(status_old & VIRTIO_CONFIG_S_DRIVER_OK)) { - if (octep_request_irqs(oct_hw)) + if (octep_request_irqs(oct_hw, octep_vdpa_intr_handler, oct_hw->nb_irqs)) status = status_old | VIRTIO_CONFIG_S_FAILED; } octep_hw_set_status(oct_hw, status); @@ -212,8 +252,10 @@ static int octep_vdpa_reset(struct vdpa_device *vdpa_dev) } octep_hw_reset(oct_hw); - if (status & VIRTIO_CONFIG_S_DRIVER_OK) + if (status & VIRTIO_CONFIG_S_DRIVER_OK) { octep_free_irqs(oct_hw); + octep_request_irqs(oct_hw, octep_vdpa_dev_event_handler, 1); + } return 0; } @@ -478,7 +520,8 @@ static void octep_vdpa_remove_vf(struct pci_dev *pdev) atomic_set(&mgmt_dev->status, OCTEP_VDPA_DEV_STATUS_UNINIT); cancel_work_sync(&mgmt_dev->setup_task); - if (status == OCTEP_VDPA_DEV_STATUS_READY) + if ((status == OCTEP_VDPA_DEV_STATUS_READY) || (status == OCTEP_VDPA_DEV_STATUS_ADDED) || + (status == OCTEP_VDPA_DEV_STATUS_REMOVED)) vdpa_mgmtdev_unregister(&mgmt_dev->mdev); if (oct_hw->base[OCTEP_HW_CAPS_BAR]) @@ -488,6 +531,7 @@ static void octep_vdpa_remove_vf(struct pci_dev *pdev) octep_iounmap_region(pdev, oct_hw->base, OCTEP_HW_MBOX_BAR); octep_vdpa_vf_bar_shrink(pdev); + octep_free_irqs(oct_hw); } static void octep_vdpa_remove(struct pci_dev *pdev) @@ -521,6 +565,7 @@ static int octep_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, oct_vdpa->vdpa.mdev = mdev; oct_vdpa->oct_hw = oct_hw; vdpa_dev = &oct_vdpa->vdpa; + mgmt_dev->oct_vdpa = oct_vdpa; device_features = oct_hw->features; if (config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) { @@ -554,6 +599,7 @@ static int octep_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, dev_err(&pdev->dev, "Failed to register to vDPA bus"); goto vdpa_dev_put; } + atomic_set(&mgmt_dev->status, OCTEP_VDPA_DEV_STATUS_ADDED); return 0; vdpa_dev_put: @@ -563,7 +609,9 @@ static int octep_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, static void octep_vdpa_dev_del(struct vdpa_mgmt_dev *mdev, struct vdpa_device *vdpa_dev) { + struct octep_vdpa_mgmt_dev *mgmt_dev = container_of(mdev, struct octep_vdpa_mgmt_dev, mdev); _vdpa_unregister_device(vdpa_dev); + atomic_set(&mgmt_dev->status, OCTEP_VDPA_DEV_STATUS_REMOVED); } static const struct vdpa_mgmtdev_ops octep_vdpa_mgmt_dev_ops = { @@ -588,6 +636,36 @@ static struct virtio_device_id id_table[] = { { 0 }, }; +static void octep_event_work(struct work_struct *work) +{ + struct octep_vdpa_event_wk *wk = container_of(work, struct octep_vdpa_event_wk, work); + struct octep_vdpa_mgmt_dev *mgmt_dev = (struct octep_vdpa_mgmt_dev *)wk->ctxptr; + u8 __iomem *addr = mgmt_dev->oct_hw.base[OCTEP_HW_MBOX_BAR]; + u8 event = readb(addr + OCTEP_VF_EVENT_REG(0)); + struct vdpa_dev_set_config config = {0}; + char name[OCTEP_VDPA_NAME_BUFSIZE]; + int ret = 0; + + switch (event) { + case OCTEP_VDPA_DEV_ADD_EVENT: + if (atomic_read(&mgmt_dev->status) != OCTEP_VDPA_DEV_STATUS_ADDED) { + snprintf(name, sizeof(name), "%s-%x", "vdpa", mgmt_dev->pdev->devfn); + ret = octep_vdpa_dev_add(&mgmt_dev->mdev, name, &config); + } + break; + case OCTEP_VDPA_DEV_DEL_EVENT: + if (atomic_read(&mgmt_dev->status) == OCTEP_VDPA_DEV_STATUS_ADDED) + octep_vdpa_dev_del(&mgmt_dev->mdev, &mgmt_dev->oct_vdpa->vdpa); + break; + default: + break; + } + + event = ret ? OCTEP_VDPA_DEV_EVENT_NACK : OCTEP_VDPA_DEV_EVENT_ACK; + writeb(event, addr + OCTEP_VF_EVENT_REG(0)); + writeb(OCTEP_VDPA_DEV_EVENT_DONE, addr + OCTEP_VF_EVENT_STATE(0)); +} + static void octep_vdpa_setup_task(struct work_struct *work) { struct octep_vdpa_mgmt_dev *mgmt_dev = container_of(work, struct octep_vdpa_mgmt_dev, @@ -653,6 +731,9 @@ static void octep_vdpa_setup_task(struct work_struct *work) } atomic_set(&mgmt_dev->status, OCTEP_VDPA_DEV_STATUS_READY); + INIT_WORK(&mgmt_dev->event_wk.work, octep_event_work); + mgmt_dev->event_wk.ctxptr = mgmt_dev; + octep_request_irqs(&mgmt_dev->oct_hw, octep_vdpa_dev_event_handler, 1); return; -- 2.25.1
