net/mlx5: support connection tracking between two ports
[dpdk.git] / drivers / common / octeontx2 / otx2_mbox.c
index cb03f65..6df1e8e 100644 (file)
@@ -9,8 +9,10 @@
 
 #include <rte_atomic.h>
 #include <rte_cycles.h>
+#include <rte_malloc.h>
 
 #include "otx2_mbox.h"
+#include "otx2_dev.h"
 
 #define RVU_AF_AFPF_MBOX0      (0x02000)
 #define RVU_AF_AFPF_MBOX1      (0x02008)
 #define        RVU_VF_VFPF_MBOX0       (0x0000)
 #define        RVU_VF_VFPF_MBOX1       (0x0008)
 
+static inline uint16_t
+msgs_offset(void)
+{
+       return RTE_ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN);
+}
+
 void
 otx2_mbox_fini(struct otx2_mbox *mbox)
 {
        mbox->reg_base = 0;
        mbox->hwbase = 0;
-       free(mbox->dev);
+       rte_free(mbox->dev);
        mbox->dev = NULL;
 }
 
@@ -53,12 +61,13 @@ otx2_mbox_reset(struct otx2_mbox *mbox, int devid)
 }
 
 int
-otx2_mbox_init(struct otx2_mbox *mbox, uintptr_t hwbase,
-              uintptr_t reg_base, int direction, int ndevs)
+otx2_mbox_init(struct otx2_mbox *mbox, uintptr_t hwbase, uintptr_t reg_base,
+              int direction, int ndevs, uint64_t intr_offset)
 {
        struct otx2_mbox_dev *mdev;
        int devid;
 
+       mbox->intr_offset = intr_offset;
        mbox->reg_base = reg_base;
        mbox->hwbase = hwbase;
 
@@ -120,7 +129,9 @@ otx2_mbox_init(struct otx2_mbox *mbox, uintptr_t hwbase,
                return -ENODEV;
        }
 
-       mbox->dev = malloc(ndevs * sizeof(struct otx2_mbox_dev));
+       mbox->dev = rte_zmalloc("mbox dev",
+                               ndevs * sizeof(struct otx2_mbox_dev),
+                               OTX2_ALIGN);
        if (!mbox->dev) {
                otx2_mbox_fini(mbox);
                return -ENOMEM;
@@ -136,3 +147,319 @@ otx2_mbox_init(struct otx2_mbox *mbox, uintptr_t hwbase,
 
        return 0;
 }
+
+/**
+ * @internal
+ * Allocate a message response
+ */
+struct mbox_msghdr *
+otx2_mbox_alloc_msg_rsp(struct otx2_mbox *mbox, int devid, int size,
+                       int size_rsp)
+{
+       struct otx2_mbox_dev *mdev = &mbox->dev[devid];
+       struct mbox_msghdr *msghdr = NULL;
+
+       rte_spinlock_lock(&mdev->mbox_lock);
+       size = RTE_ALIGN(size, MBOX_MSG_ALIGN);
+       size_rsp = RTE_ALIGN(size_rsp, MBOX_MSG_ALIGN);
+       /* Check if there is space in mailbox */
+       if ((mdev->msg_size + size) > mbox->tx_size - msgs_offset())
+               goto exit;
+       if ((mdev->rsp_size + size_rsp) > mbox->rx_size - msgs_offset())
+               goto exit;
+       if (mdev->msg_size == 0)
+               mdev->num_msgs = 0;
+       mdev->num_msgs++;
+
+       msghdr = (struct mbox_msghdr *)(((uintptr_t)mdev->mbase +
+                       mbox->tx_start + msgs_offset() + mdev->msg_size));
+
+       /* Clear the whole msg region */
+       otx2_mbox_memset(msghdr, 0, sizeof(*msghdr) + size);
+       /* Init message header with reset values */
+       msghdr->ver = OTX2_MBOX_VERSION;
+       mdev->msg_size += size;
+       mdev->rsp_size += size_rsp;
+       msghdr->next_msgoff = mdev->msg_size + msgs_offset();
+exit:
+       rte_spinlock_unlock(&mdev->mbox_lock);
+
+       return msghdr;
+}
+
+/**
+ * @internal
+ * Send a mailbox message
+ */
+void
+otx2_mbox_msg_send(struct otx2_mbox *mbox, int devid)
+{
+       struct otx2_mbox_dev *mdev = &mbox->dev[devid];
+       struct mbox_hdr *tx_hdr =
+               (struct mbox_hdr *)((uintptr_t)mdev->mbase + mbox->tx_start);
+       struct mbox_hdr *rx_hdr =
+               (struct mbox_hdr *)((uintptr_t)mdev->mbase + mbox->rx_start);
+
+       /* Reset header for next messages */
+       tx_hdr->msg_size = mdev->msg_size;
+       mdev->msg_size = 0;
+       mdev->rsp_size = 0;
+       mdev->msgs_acked = 0;
+
+       /* num_msgs != 0 signals to the peer that the buffer has a number of
+        * messages. So this should be written after copying txmem
+        */
+       tx_hdr->num_msgs = mdev->num_msgs;
+       rx_hdr->num_msgs = 0;
+
+       /* Sync mbox data into memory */
+       rte_wmb();
+
+       /* The interrupt should be fired after num_msgs is written
+        * to the shared memory
+        */
+       rte_write64(1, (volatile void *)(mbox->reg_base +
+               (mbox->trigger | (devid << mbox->tr_shift))));
+}
+
+/**
+ * @internal
+ * Wait and get mailbox response
+ */
+int
+otx2_mbox_get_rsp(struct otx2_mbox *mbox, int devid, void **msg)
+{
+       struct otx2_mbox_dev *mdev = &mbox->dev[devid];
+       struct mbox_msghdr *msghdr;
+       uint64_t offset;
+       int rc;
+
+       rc = otx2_mbox_wait_for_rsp(mbox, devid);
+       if (rc != 1)
+               return -EIO;
+
+       rte_rmb();
+
+       offset = mbox->rx_start +
+               RTE_ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN);
+       msghdr = (struct mbox_msghdr *)((uintptr_t)mdev->mbase + offset);
+       if (msg != NULL)
+               *msg = msghdr;
+
+       return msghdr->rc;
+}
+
+/**
+ * Polling for given wait time to get mailbox response
+ */
+static int
+mbox_poll(struct otx2_mbox *mbox, uint32_t wait)
+{
+       uint32_t timeout = 0, sleep = 1;
+       uint32_t wait_us = wait * 1000;
+       uint64_t rsp_reg = 0;
+       uintptr_t reg_addr;
+
+       reg_addr = mbox->reg_base + mbox->intr_offset;
+       do {
+               rsp_reg = otx2_read64(reg_addr);
+
+               if (timeout >= wait_us)
+                       return -ETIMEDOUT;
+
+               rte_delay_us(sleep);
+               timeout += sleep;
+       } while (!rsp_reg);
+
+       rte_smp_rmb();
+
+       /* Clear interrupt */
+       otx2_write64(rsp_reg, reg_addr);
+
+       /* Reset mbox */
+       otx2_mbox_reset(mbox, 0);
+
+       return 0;
+}
+
+/**
+ * @internal
+ * Wait and get mailbox response with timeout
+ */
+int
+otx2_mbox_get_rsp_tmo(struct otx2_mbox *mbox, int devid, void **msg,
+                     uint32_t tmo)
+{
+       struct otx2_mbox_dev *mdev = &mbox->dev[devid];
+       struct mbox_msghdr *msghdr;
+       uint64_t offset;
+       int rc;
+
+       rc = otx2_mbox_wait_for_rsp_tmo(mbox, devid, tmo);
+       if (rc != 1)
+               return -EIO;
+
+       rte_rmb();
+
+       offset = mbox->rx_start +
+                       RTE_ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN);
+       msghdr = (struct mbox_msghdr *)((uintptr_t)mdev->mbase + offset);
+       if (msg != NULL)
+               *msg = msghdr;
+
+       return msghdr->rc;
+}
+
+static int
+mbox_wait(struct otx2_mbox *mbox, int devid, uint32_t rst_timo)
+{
+       volatile struct otx2_mbox_dev *mdev = &mbox->dev[devid];
+       uint32_t timeout = 0, sleep = 1;
+
+       rst_timo  = rst_timo * 1000; /* Milli seconds to micro seconds */
+       while (mdev->num_msgs > mdev->msgs_acked) {
+               rte_delay_us(sleep);
+               timeout += sleep;
+               if (timeout >= rst_timo) {
+                       struct mbox_hdr *tx_hdr =
+                               (struct mbox_hdr *)((uintptr_t)mdev->mbase +
+                                                       mbox->tx_start);
+                       struct mbox_hdr *rx_hdr =
+                               (struct mbox_hdr *)((uintptr_t)mdev->mbase +
+                                                       mbox->rx_start);
+
+                       otx2_err("MBOX[devid: %d] message wait timeout %d, "
+                                "num_msgs: %d, msgs_acked: %d "
+                                "(tx/rx num_msgs: %d/%d), msg_size: %d, "
+                                "rsp_size: %d",
+                                devid, timeout, mdev->num_msgs,
+                                mdev->msgs_acked, tx_hdr->num_msgs,
+                                rx_hdr->num_msgs, mdev->msg_size,
+                                mdev->rsp_size);
+
+                       return -EIO;
+               }
+               rte_rmb();
+       }
+       return 0;
+}
+
+int
+otx2_mbox_wait_for_rsp_tmo(struct otx2_mbox *mbox, int devid, uint32_t tmo)
+{
+       struct otx2_mbox_dev *mdev = &mbox->dev[devid];
+       int rc = 0;
+
+       /* Sync with mbox region */
+       rte_rmb();
+
+       if (mbox->trigger == RVU_PF_VFX_PFVF_MBOX1 ||
+               mbox->trigger == RVU_PF_VFX_PFVF_MBOX0) {
+               /* In case of VF, Wait a bit more to account round trip delay */
+               tmo = tmo * 2;
+       }
+
+       /* Wait message */
+       if (rte_thread_is_intr())
+               rc = mbox_poll(mbox, tmo);
+       else
+               rc = mbox_wait(mbox, devid, tmo);
+
+       if (!rc)
+               rc = mdev->num_msgs;
+
+       return rc;
+}
+
+/**
+ * @internal
+ * Wait for the mailbox response
+ */
+int
+otx2_mbox_wait_for_rsp(struct otx2_mbox *mbox, int devid)
+{
+       return otx2_mbox_wait_for_rsp_tmo(mbox, devid, MBOX_RSP_TIMEOUT);
+}
+
+int
+otx2_mbox_get_availmem(struct otx2_mbox *mbox, int devid)
+{
+       struct otx2_mbox_dev *mdev = &mbox->dev[devid];
+       int avail;
+
+       rte_spinlock_lock(&mdev->mbox_lock);
+       avail = mbox->tx_size - mdev->msg_size - msgs_offset();
+       rte_spinlock_unlock(&mdev->mbox_lock);
+
+       return avail;
+}
+
+int
+otx2_send_ready_msg(struct otx2_mbox *mbox, uint16_t *pcifunc)
+{
+       struct ready_msg_rsp *rsp;
+       int rc;
+
+       otx2_mbox_alloc_msg_ready(mbox);
+
+       otx2_mbox_msg_send(mbox, 0);
+       rc = otx2_mbox_get_rsp(mbox, 0, (void *)&rsp);
+       if (rc)
+               return rc;
+
+       if (rsp->hdr.ver != OTX2_MBOX_VERSION) {
+               otx2_err("Incompatible MBox versions(AF: 0x%04x DPDK: 0x%04x)",
+                         rsp->hdr.ver, OTX2_MBOX_VERSION);
+               return -EPIPE;
+       }
+
+       if (pcifunc)
+               *pcifunc = rsp->hdr.pcifunc;
+
+       return 0;
+}
+
+int
+otx2_reply_invalid_msg(struct otx2_mbox *mbox, int devid, uint16_t pcifunc,
+                      uint16_t id)
+{
+       struct msg_rsp *rsp;
+
+       rsp = (struct msg_rsp *)otx2_mbox_alloc_msg(mbox, devid, sizeof(*rsp));
+       if (!rsp)
+               return -ENOMEM;
+       rsp->hdr.id = id;
+       rsp->hdr.sig = OTX2_MBOX_RSP_SIG;
+       rsp->hdr.rc = MBOX_MSG_INVALID;
+       rsp->hdr.pcifunc = pcifunc;
+
+       return 0;
+}
+
+/**
+ * @internal
+ * Convert mail box ID to name
+ */
+const char *otx2_mbox_id2name(uint16_t id)
+{
+       switch (id) {
+#define M(_name, _id, _1, _2, _3) case _id: return # _name;
+       MBOX_MESSAGES
+       MBOX_UP_CGX_MESSAGES
+#undef M
+       default :
+               return "INVALID ID";
+       }
+}
+
+int otx2_mbox_id2size(uint16_t id)
+{
+       switch (id) {
+#define M(_1, _id, _2, _req_type, _3) case _id: return sizeof(struct _req_type);
+       MBOX_MESSAGES
+       MBOX_UP_CGX_MESSAGES
+#undef M
+       default :
+               return 0;
+       }
+}