octeontx2-af: Add mailbox IRQ and msg handlers
authorSunil Goutham <sgoutham@marvell.com>
Wed, 10 Oct 2018 12:44:25 +0000 (18:14 +0530)
committerDavid S. Miller <davem@davemloft.net>
Wed, 10 Oct 2018 17:06:01 +0000 (10:06 -0700)
This patch adds support for mailbox interrupt and message
handling. Mapped mailbox region and registered a workqueue
for message handling. Enabled mailbox IRQ of RVU PFs
and registered a interrupt handler. When IRQ is triggered
work is added to the mbox workqueue for msgs to get processed.

Signed-off-by: Sunil Goutham <sgoutham@marvell.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/marvell/octeontx2/af/mbox.h
drivers/net/ethernet/marvell/octeontx2/af/rvu.c
drivers/net/ethernet/marvell/octeontx2/af/rvu.h
drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h

index 8e205fdaa4647033774098f6764274e988e52fd2..fc593f0a4333840b486cd85636c887e971293b0b 100644 (file)
@@ -33,6 +33,8 @@
 # error "incorrect mailbox area sizes"
 #endif
 
+#define INTR_MASK(pfvfs) ((pfvfs < 64) ? (BIT_ULL(pfvfs) - 1) : (~0ull))
+
 #define MBOX_RSP_TIMEOUT       1000 /* in ms, Time to wait for mbox response */
 
 #define MBOX_MSG_ALIGN         16  /* Align mbox msg start to 16bytes */
@@ -90,8 +92,9 @@ struct mbox_msghdr {
 
 void otx2_mbox_reset(struct otx2_mbox *mbox, int devid);
 void otx2_mbox_destroy(struct otx2_mbox *mbox);
-int otx2_mbox_init(struct otx2_mbox *mbox, void *hwbase, struct pci_dev *pdev,
-                  void *reg_base, int direction, int ndevs);
+int otx2_mbox_init(struct otx2_mbox *mbox, void __force *hwbase,
+                  struct pci_dev *pdev, void __force *reg_base,
+                  int direction, int ndevs);
 void otx2_mbox_msg_send(struct otx2_mbox *mbox, int devid);
 int otx2_mbox_wait_for_rsp(struct otx2_mbox *mbox, int devid);
 int otx2_mbox_busy_poll_for_rsp(struct otx2_mbox *mbox, int devid);
@@ -115,7 +118,7 @@ static inline struct mbox_msghdr *otx2_mbox_alloc_msg(struct otx2_mbox *mbox,
 #define MBOX_MSG_MAX                           0xFFFF
 
 #define MBOX_MESSAGES                                                  \
-M(READY,               0x001, msg_req, msg_rsp)
+M(READY,               0x001, msg_req, ready_msg_rsp)
 
 enum {
 #define M(_name, _id, _1, _2) MBOX_MSG_ ## _name = _id,
@@ -139,4 +142,9 @@ struct msg_rsp {
        struct mbox_msghdr hdr;
 };
 
+struct ready_msg_rsp {
+       struct mbox_msghdr hdr;
+       u16    sclk_feq;        /* SCLK frequency */
+};
+
 #endif /* MBOX_H */
index cd156537c58043e3de9e26e37a9542511caccdb5..2e70947bbb9d998cbd940229bbe5e30c046fe5d6 100644 (file)
@@ -257,6 +257,245 @@ cpt:
        return 0;
 }
 
+static int rvu_process_mbox_msg(struct rvu *rvu, int devid,
+                               struct mbox_msghdr *req)
+{
+       /* Check if valid, if not reply with a invalid msg */
+       if (req->sig != OTX2_MBOX_REQ_SIG)
+               goto bad_message;
+
+       if (req->id == MBOX_MSG_READY)
+               return 0;
+
+bad_message:
+       otx2_reply_invalid_msg(&rvu->mbox, devid, req->pcifunc,
+                              req->id);
+       return -ENODEV;
+}
+
+static void rvu_mbox_handler(struct work_struct *work)
+{
+       struct rvu_work *mwork = container_of(work, struct rvu_work, work);
+       struct rvu *rvu = mwork->rvu;
+       struct otx2_mbox_dev *mdev;
+       struct mbox_hdr *req_hdr;
+       struct mbox_msghdr *msg;
+       struct otx2_mbox *mbox;
+       int offset, id, err;
+       u16 pf;
+
+       mbox = &rvu->mbox;
+       pf = mwork - rvu->mbox_wrk;
+       mdev = &mbox->dev[pf];
+
+       /* Process received mbox messages */
+       req_hdr = mdev->mbase + mbox->rx_start;
+       if (req_hdr->num_msgs == 0)
+               return;
+
+       offset = mbox->rx_start + ALIGN(sizeof(*req_hdr), MBOX_MSG_ALIGN);
+
+       for (id = 0; id < req_hdr->num_msgs; id++) {
+               msg = mdev->mbase + offset;
+
+               /* Set which PF sent this message based on mbox IRQ */
+               msg->pcifunc &= ~(RVU_PFVF_PF_MASK << RVU_PFVF_PF_SHIFT);
+               msg->pcifunc |= (pf << RVU_PFVF_PF_SHIFT);
+               err = rvu_process_mbox_msg(rvu, pf, msg);
+               if (!err) {
+                       offset = mbox->rx_start + msg->next_msgoff;
+                       continue;
+               }
+
+               if (msg->pcifunc & RVU_PFVF_FUNC_MASK)
+                       dev_warn(rvu->dev, "Error %d when processing message %s (0x%x) from PF%d:VF%d\n",
+                                err, otx2_mbox_id2name(msg->id), msg->id, pf,
+                                (msg->pcifunc & RVU_PFVF_FUNC_MASK) - 1);
+               else
+                       dev_warn(rvu->dev, "Error %d when processing message %s (0x%x) from PF%d\n",
+                                err, otx2_mbox_id2name(msg->id), msg->id, pf);
+       }
+
+       /* Send mbox responses to PF */
+       otx2_mbox_msg_send(mbox, pf);
+}
+
+static int rvu_mbox_init(struct rvu *rvu)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+       void __iomem *hwbase = NULL;
+       struct rvu_work *mwork;
+       u64 bar4_addr;
+       int err, pf;
+
+       rvu->mbox_wq = alloc_workqueue("rvu_afpf_mailbox",
+                                      WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM,
+                                      hw->total_pfs);
+       if (!rvu->mbox_wq)
+               return -ENOMEM;
+
+       rvu->mbox_wrk = devm_kcalloc(rvu->dev, hw->total_pfs,
+                                    sizeof(struct rvu_work), GFP_KERNEL);
+       if (!rvu->mbox_wrk) {
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       /* Map mbox region shared with PFs */
+       bar4_addr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PF_BAR4_ADDR);
+       /* Mailbox is a reserved memory (in RAM) region shared between
+        * RVU devices, shouldn't be mapped as device memory to allow
+        * unaligned accesses.
+        */
+       hwbase = ioremap_wc(bar4_addr, MBOX_SIZE * hw->total_pfs);
+       if (!hwbase) {
+               dev_err(rvu->dev, "Unable to map mailbox region\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       err = otx2_mbox_init(&rvu->mbox, hwbase, rvu->pdev, rvu->afreg_base,
+                            MBOX_DIR_AFPF, hw->total_pfs);
+       if (err)
+               goto exit;
+
+       for (pf = 0; pf < hw->total_pfs; pf++) {
+               mwork = &rvu->mbox_wrk[pf];
+               mwork->rvu = rvu;
+               INIT_WORK(&mwork->work, rvu_mbox_handler);
+       }
+
+       return 0;
+exit:
+       if (hwbase)
+               iounmap((void __iomem *)hwbase);
+       destroy_workqueue(rvu->mbox_wq);
+       return err;
+}
+
+static void rvu_mbox_destroy(struct rvu *rvu)
+{
+       if (rvu->mbox_wq) {
+               flush_workqueue(rvu->mbox_wq);
+               destroy_workqueue(rvu->mbox_wq);
+               rvu->mbox_wq = NULL;
+       }
+
+       if (rvu->mbox.hwbase)
+               iounmap((void __iomem *)rvu->mbox.hwbase);
+
+       otx2_mbox_destroy(&rvu->mbox);
+}
+
+static irqreturn_t rvu_mbox_intr_handler(int irq, void *rvu_irq)
+{
+       struct rvu *rvu = (struct rvu *)rvu_irq;
+       struct otx2_mbox_dev *mdev;
+       struct otx2_mbox *mbox;
+       struct mbox_hdr *hdr;
+       u64 intr;
+       u8  pf;
+
+       intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT);
+       /* Clear interrupts */
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT, intr);
+
+       /* Sync with mbox memory region */
+       smp_wmb();
+
+       for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
+               if (intr & (1ULL << pf)) {
+                       mbox = &rvu->mbox;
+                       mdev = &mbox->dev[pf];
+                       hdr = mdev->mbase + mbox->rx_start;
+                       if (hdr->num_msgs)
+                               queue_work(rvu->mbox_wq,
+                                          &rvu->mbox_wrk[pf].work);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void rvu_enable_mbox_intr(struct rvu *rvu)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+
+       /* Clear spurious irqs, if any */
+       rvu_write64(rvu, BLKADDR_RVUM,
+                   RVU_AF_PFAF_MBOX_INT, INTR_MASK(hw->total_pfs));
+
+       /* Enable mailbox interrupt for all PFs except PF0 i.e AF itself */
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT_ENA_W1S,
+                   INTR_MASK(hw->total_pfs) & ~1ULL);
+}
+
+static void rvu_unregister_interrupts(struct rvu *rvu)
+{
+       int irq;
+
+       /* Disable the Mbox interrupt */
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT_ENA_W1C,
+                   INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
+
+       for (irq = 0; irq < rvu->num_vec; irq++) {
+               if (rvu->irq_allocated[irq])
+                       free_irq(pci_irq_vector(rvu->pdev, irq), rvu);
+       }
+
+       pci_free_irq_vectors(rvu->pdev);
+       rvu->num_vec = 0;
+}
+
+static int rvu_register_interrupts(struct rvu *rvu)
+{
+       int ret;
+
+       rvu->num_vec = pci_msix_vec_count(rvu->pdev);
+
+       rvu->irq_name = devm_kmalloc_array(rvu->dev, rvu->num_vec,
+                                          NAME_SIZE, GFP_KERNEL);
+       if (!rvu->irq_name)
+               return -ENOMEM;
+
+       rvu->irq_allocated = devm_kcalloc(rvu->dev, rvu->num_vec,
+                                         sizeof(bool), GFP_KERNEL);
+       if (!rvu->irq_allocated)
+               return -ENOMEM;
+
+       /* Enable MSI-X */
+       ret = pci_alloc_irq_vectors(rvu->pdev, rvu->num_vec,
+                                   rvu->num_vec, PCI_IRQ_MSIX);
+       if (ret < 0) {
+               dev_err(rvu->dev,
+                       "RVUAF: Request for %d msix vectors failed, ret %d\n",
+                       rvu->num_vec, ret);
+               return ret;
+       }
+
+       /* Register mailbox interrupt handler */
+       sprintf(&rvu->irq_name[RVU_AF_INT_VEC_MBOX * NAME_SIZE], "RVUAF Mbox");
+       ret = request_irq(pci_irq_vector(rvu->pdev, RVU_AF_INT_VEC_MBOX),
+                         rvu_mbox_intr_handler, 0,
+                         &rvu->irq_name[RVU_AF_INT_VEC_MBOX * NAME_SIZE], rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for mbox irq\n");
+               goto fail;
+       }
+
+       rvu->irq_allocated[RVU_AF_INT_VEC_MBOX] = true;
+
+       /* Enable mailbox interrupts from all PFs */
+       rvu_enable_mbox_intr(rvu);
+
+       return 0;
+
+fail:
+       pci_free_irq_vectors(rvu->pdev);
+       return ret;
+}
+
 static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        struct device *dev = &pdev->dev;
@@ -319,8 +558,21 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (err)
                goto err_release_regions;
 
+       err = rvu_mbox_init(rvu);
+       if (err)
+               goto err_hwsetup;
+
+       err = rvu_register_interrupts(rvu);
+       if (err)
+               goto err_mbox;
+
        return 0;
 
+err_mbox:
+       rvu_mbox_destroy(rvu);
+err_hwsetup:
+       rvu_reset_all_blocks(rvu);
+       rvu_free_hw_resources(rvu);
 err_release_regions:
        pci_release_regions(pdev);
 err_disable_device:
@@ -336,6 +588,8 @@ static void rvu_remove(struct pci_dev *pdev)
 {
        struct rvu *rvu = pci_get_drvdata(pdev);
 
+       rvu_unregister_interrupts(rvu);
+       rvu_mbox_destroy(rvu);
        rvu_reset_all_blocks(rvu);
        rvu_free_hw_resources(rvu);
 
index 592b820ccf72d0b113bc3b183174c5823abaa3ab..2fb9407da531cf28837a96c514bacd2ff56c98f3 100644 (file)
@@ -12,6 +12,7 @@
 #define RVU_H
 
 #include "rvu_struct.h"
+#include "mbox.h"
 
 /* PCI device IDs */
 #define        PCI_DEVID_OCTEONTX2_RVU_AF              0xA065
 
 #define NAME_SIZE                              32
 
+/* PF_FUNC */
+#define RVU_PFVF_PF_SHIFT      10
+#define RVU_PFVF_PF_MASK       0x3F
+#define RVU_PFVF_FUNC_SHIFT    0
+#define RVU_PFVF_FUNC_MASK     0x3FF
+
+struct rvu_work {
+       struct  work_struct work;
+       struct  rvu *rvu;
+};
+
 struct rsrc_bmap {
        unsigned long *bmap;    /* Pointer to resource bitmap */
        u16  max;               /* Max resource id or count */
@@ -57,6 +69,16 @@ struct rvu {
        struct pci_dev          *pdev;
        struct device           *dev;
        struct rvu_hwinfo       *hw;
+
+       /* Mbox */
+       struct otx2_mbox        mbox;
+       struct rvu_work         *mbox_wrk;
+       struct workqueue_struct *mbox_wq;
+
+       /* MSI-X */
+       u16                     num_vec;
+       char                    *irq_name;
+       bool                    *irq_allocated;
 };
 
 static inline void rvu_write64(struct rvu *rvu, u64 block, u64 offset, u64 val)
index 9e11333cec341c54de9317f2ab4770dfc94f0f15..4214bc095b2795ce6944e2682fd60cf43a06ef23 100644 (file)
@@ -31,4 +31,26 @@ enum rvu_block_addr_e {
        BLK_COUNT       = 0xfULL,
 };
 
+/* RVU Admin function Interrupt Vector Enumeration */
+enum rvu_af_int_vec_e {
+       RVU_AF_INT_VEC_POISON = 0x0,
+       RVU_AF_INT_VEC_PFFLR  = 0x1,
+       RVU_AF_INT_VEC_PFME   = 0x2,
+       RVU_AF_INT_VEC_GEN    = 0x3,
+       RVU_AF_INT_VEC_MBOX   = 0x4,
+};
+
+/**
+ * RVU PF Interrupt Vector Enumeration
+ */
+enum rvu_pf_int_vec_e {
+       RVU_PF_INT_VEC_VFFLR0     = 0x0,
+       RVU_PF_INT_VEC_VFFLR1     = 0x1,
+       RVU_PF_INT_VEC_VFME0      = 0x2,
+       RVU_PF_INT_VEC_VFME1      = 0x3,
+       RVU_PF_INT_VEC_VFPF_MBOX0 = 0x4,
+       RVU_PF_INT_VEC_VFPF_MBOX1 = 0x5,
+       RVU_PF_INT_VEC_AFPF_MBOX  = 0x6,
+};
+
 #endif /* RVU_STRUCT_H */