octeon_ep: Add mailbox for control commands
authorVeerasenareddy Burru <vburru@marvell.com>
Wed, 13 Apr 2022 03:34:59 +0000 (20:34 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 13 Apr 2022 11:56:32 +0000 (12:56 +0100)
Add mailbox between host and NIC to send control commands from host to
NIC and receive responses and notifications from NIC to host driver,
like link status update.

Signed-off-by: Veerasenareddy Burru <vburru@marvell.com>
Signed-off-by: Abhijit Ayarekar <aayarekar@marvell.com>
Signed-off-by: Satananda Burla <sburla@marvell.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.c
drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.h
drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_net.c

index 5479f64d5bfc6a85e17dca0ae2a35b0c80456b99..8c196dadfad069690209dd5ab3b5d5d1deaed1ea 100644 (file)
 #define OCTEP_CTRL_MBOX_Q_OFFSET(m, i)                 ((m) + \
                                                         (sizeof(struct octep_ctrl_mbox_msg) * (i)))
 
+static u32 octep_ctrl_mbox_circq_inc(u32 index, u32 mask)
+{
+       return (index + 1) & mask;
+}
+
+static u32 octep_ctrl_mbox_circq_space(u32 pi, u32 ci, u32 mask)
+{
+       return mask - ((pi - ci) & mask);
+}
+
+static u32 octep_ctrl_mbox_circq_depth(u32 pi, u32 ci, u32 mask)
+{
+       return ((pi - ci) & mask);
+}
+
 int octep_ctrl_mbox_init(struct octep_ctrl_mbox *mbox)
 {
-       return -EINVAL;
+       u64 version, magic_num, status;
+
+       if (!mbox)
+               return -EINVAL;
+
+       if (!mbox->barmem) {
+               pr_info("octep_ctrl_mbox : Invalid barmem %p\n", mbox->barmem);
+               return -EINVAL;
+       }
+
+       magic_num = readq(OCTEP_CTRL_MBOX_INFO_MAGIC_NUM_OFFSET(mbox->barmem));
+       if (magic_num != OCTEP_CTRL_MBOX_MAGIC_NUMBER) {
+               pr_info("octep_ctrl_mbox : Invalid magic number %llx\n", magic_num);
+               return -EINVAL;
+       }
+
+       version = readq(OCTEP_CTRL_MBOX_INFO_FW_VERSION_OFFSET(mbox->barmem));
+       if (version != OCTEP_DRV_VERSION) {
+               pr_info("octep_ctrl_mbox : Firmware version mismatch %llx != %x\n",
+                       version, OCTEP_DRV_VERSION);
+               return -EINVAL;
+       }
+
+       status = readq(OCTEP_CTRL_MBOX_INFO_FW_STATUS_OFFSET(mbox->barmem));
+       if (status != OCTEP_CTRL_MBOX_STATUS_READY) {
+               pr_info("octep_ctrl_mbox : Firmware is not ready.\n");
+               return -EINVAL;
+       }
+
+       mbox->barmem_sz = readl(OCTEP_CTRL_MBOX_INFO_BARMEM_SZ_OFFSET(mbox->barmem));
+
+       writeq(mbox->version, OCTEP_CTRL_MBOX_INFO_HOST_VERSION_OFFSET(mbox->barmem));
+       writeq(OCTEP_CTRL_MBOX_STATUS_INIT, OCTEP_CTRL_MBOX_INFO_HOST_STATUS_OFFSET(mbox->barmem));
+
+       mbox->h2fq.elem_cnt = readl(OCTEP_CTRL_MBOX_H2FQ_ELEM_CNT_OFFSET(mbox->barmem));
+       mbox->h2fq.elem_sz = readl(OCTEP_CTRL_MBOX_H2FQ_ELEM_SZ_OFFSET(mbox->barmem));
+       mbox->h2fq.mask = (mbox->h2fq.elem_cnt - 1);
+       mutex_init(&mbox->h2fq_lock);
+
+       mbox->f2hq.elem_cnt = readl(OCTEP_CTRL_MBOX_F2HQ_ELEM_CNT_OFFSET(mbox->barmem));
+       mbox->f2hq.elem_sz = readl(OCTEP_CTRL_MBOX_F2HQ_ELEM_SZ_OFFSET(mbox->barmem));
+       mbox->f2hq.mask = (mbox->f2hq.elem_cnt - 1);
+       mutex_init(&mbox->f2hq_lock);
+
+       mbox->h2fq.hw_prod = OCTEP_CTRL_MBOX_H2FQ_PROD_OFFSET(mbox->barmem);
+       mbox->h2fq.hw_cons = OCTEP_CTRL_MBOX_H2FQ_CONS_OFFSET(mbox->barmem);
+       mbox->h2fq.hw_q = mbox->barmem +
+                         OCTEP_CTRL_MBOX_INFO_SZ +
+                         OCTEP_CTRL_MBOX_H2FQ_INFO_SZ +
+                         OCTEP_CTRL_MBOX_F2HQ_INFO_SZ;
+
+       mbox->f2hq.hw_prod = OCTEP_CTRL_MBOX_F2HQ_PROD_OFFSET(mbox->barmem);
+       mbox->f2hq.hw_cons = OCTEP_CTRL_MBOX_F2HQ_CONS_OFFSET(mbox->barmem);
+       mbox->f2hq.hw_q = mbox->h2fq.hw_q +
+                         ((mbox->h2fq.elem_sz + sizeof(union octep_ctrl_mbox_msg_hdr)) *
+                          mbox->h2fq.elem_cnt);
+
+       /* ensure ready state is seen after everything is initialized */
+       wmb();
+       writeq(OCTEP_CTRL_MBOX_STATUS_READY, OCTEP_CTRL_MBOX_INFO_HOST_STATUS_OFFSET(mbox->barmem));
+
+       pr_info("Octep ctrl mbox : Init successful.\n");
+
+       return 0;
+}
+
+int octep_ctrl_mbox_send(struct octep_ctrl_mbox *mbox, struct octep_ctrl_mbox_msg *msg)
+{
+       unsigned long timeout = msecs_to_jiffies(OCTEP_CTRL_MBOX_MSG_TIMEOUT_MS);
+       unsigned long period = msecs_to_jiffies(OCTEP_CTRL_MBOX_MSG_WAIT_MS);
+       struct octep_ctrl_mbox_q *q;
+       unsigned long expire;
+       u64 *mbuf, *word0;
+       u8 __iomem *qidx;
+       u16 pi, ci;
+       int i;
+
+       if (!mbox || !msg)
+               return -EINVAL;
+
+       q = &mbox->h2fq;
+       pi = readl(q->hw_prod);
+       ci = readl(q->hw_cons);
+
+       if (!octep_ctrl_mbox_circq_space(pi, ci, q->mask))
+               return -ENOMEM;
+
+       qidx = OCTEP_CTRL_MBOX_Q_OFFSET(q->hw_q, pi);
+       mbuf = (u64 *)msg->msg;
+       word0 = &msg->hdr.word0;
+
+       mutex_lock(&mbox->h2fq_lock);
+       for (i = 1; i <= msg->hdr.sizew; i++)
+               writeq(*mbuf++, (qidx + (i * 8)));
+
+       writeq(*word0, qidx);
+
+       pi = octep_ctrl_mbox_circq_inc(pi, q->mask);
+       writel(pi, q->hw_prod);
+       mutex_unlock(&mbox->h2fq_lock);
+
+       /* don't check for notification response */
+       if (msg->hdr.flags & OCTEP_CTRL_MBOX_MSG_HDR_FLAG_NOTIFY)
+               return 0;
+
+       expire = jiffies + timeout;
+       while (true) {
+               *word0 = readq(qidx);
+               if (msg->hdr.flags == OCTEP_CTRL_MBOX_MSG_HDR_FLAG_RESP)
+                       break;
+               schedule_timeout_interruptible(period);
+               if (signal_pending(current) || time_after(jiffies, expire)) {
+                       pr_info("octep_ctrl_mbox: Timed out\n");
+                       return -EBUSY;
+               }
+       }
+       mbuf = (u64 *)msg->msg;
+       for (i = 1; i <= msg->hdr.sizew; i++)
+               *mbuf++ = readq(qidx + (i * 8));
+
+       return 0;
 }
 
 int octep_ctrl_mbox_recv(struct octep_ctrl_mbox *mbox, struct octep_ctrl_mbox_msg *msg)
 {
-       return -EINVAL;
+       struct octep_ctrl_mbox_q *q;
+       u32 count, pi, ci;
+       u8 __iomem *qidx;
+       u64 *mbuf;
+       int i;
+
+       if (!mbox || !msg)
+               return -EINVAL;
+
+       q = &mbox->f2hq;
+       pi = readl(q->hw_prod);
+       ci = readl(q->hw_cons);
+       count = octep_ctrl_mbox_circq_depth(pi, ci, q->mask);
+       if (!count)
+               return -EAGAIN;
+
+       qidx = OCTEP_CTRL_MBOX_Q_OFFSET(q->hw_q, ci);
+       mbuf = (u64 *)msg->msg;
+
+       mutex_lock(&mbox->f2hq_lock);
+
+       msg->hdr.word0 = readq(qidx);
+       for (i = 1; i <= msg->hdr.sizew; i++)
+               *mbuf++ = readq(qidx + (i * 8));
+
+       ci = octep_ctrl_mbox_circq_inc(ci, q->mask);
+       writel(ci, q->hw_cons);
+
+       mutex_unlock(&mbox->f2hq_lock);
+
+       if (msg->hdr.flags != OCTEP_CTRL_MBOX_MSG_HDR_FLAG_REQ || !mbox->process_req)
+               return 0;
+
+       mbox->process_req(mbox->user_ctx, msg);
+       mbuf = (u64 *)msg->msg;
+       for (i = 1; i <= msg->hdr.sizew; i++)
+               writeq(*mbuf++, (qidx + (i * 8)));
+
+       writeq(msg->hdr.word0, qidx);
+
+       return 0;
 }
 
 int octep_ctrl_mbox_uninit(struct octep_ctrl_mbox *mbox)
 {
-       return -EINVAL;
+       if (!mbox)
+               return -EINVAL;
+
+       writeq(OCTEP_CTRL_MBOX_STATUS_UNINIT,
+              OCTEP_CTRL_MBOX_INFO_HOST_STATUS_OFFSET(mbox->barmem));
+       /* ensure uninit state is written before uninitialization */
+       wmb();
+
+       mutex_destroy(&mbox->h2fq_lock);
+       mutex_destroy(&mbox->f2hq_lock);
+
+       writeq(OCTEP_CTRL_MBOX_STATUS_INVALID,
+              OCTEP_CTRL_MBOX_INFO_HOST_STATUS_OFFSET(mbox->barmem));
+       writeq(0, OCTEP_CTRL_MBOX_INFO_HOST_VERSION_OFFSET(mbox->barmem));
+
+       pr_info("Octep ctrl mbox : Uninit successful.\n");
+
+       return 0;
 }
index d5ad58c6bbaa6d0e7c095f54317dcab930f0c6fe..30f497f0bc261b47a39f82aa88031d674f63dbe4 100644 (file)
@@ -107,11 +107,11 @@ struct octep_ctrl_mbox_q {
        /* q mask */
        u16 mask;
        /* producer address in bar mem */
-       void __iomem *hw_prod;
+       u8 __iomem *hw_prod;
        /* consumer address in bar mem */
-       void __iomem *hw_cons;
+       u8 __iomem *hw_cons;
        /* q base address in bar mem */
-       void __iomem *hw_q;
+       u8 __iomem *hw_q;
 };
 
 struct octep_ctrl_mbox {
index 021f888d8f6d64f02dcd6faa95572ac91309d28b..c3aca7b2775bd75137c22f2bf355e56d55ccc94e 100644 (file)
 
 int octep_get_link_status(struct octep_device *oct)
 {
-       return 0;
+       struct octep_ctrl_net_h2f_req req = {};
+       struct octep_ctrl_net_h2f_resp *resp;
+       struct octep_ctrl_mbox_msg msg = {};
+       int err;
+
+       req.hdr.cmd = OCTEP_CTRL_NET_H2F_CMD_LINK_STATUS;
+       req.link.cmd = OCTEP_CTRL_NET_CMD_GET;
+
+       msg.hdr.flags = OCTEP_CTRL_MBOX_MSG_HDR_FLAG_REQ;
+       msg.hdr.sizew = OCTEP_CTRL_NET_H2F_STATE_REQ_SZW;
+       msg.msg = &req;
+       err = octep_ctrl_mbox_send(&oct->ctrl_mbox, &msg);
+       if (err)
+               return err;
+
+       resp = (struct octep_ctrl_net_h2f_resp *)&req;
+       return resp->link.state;
 }
 
 void octep_set_link_status(struct octep_device *oct, bool up)
 {
+       struct octep_ctrl_net_h2f_req req = {};
+       struct octep_ctrl_mbox_msg msg = {};
+
+       req.hdr.cmd = OCTEP_CTRL_NET_H2F_CMD_LINK_STATUS;
+       req.link.cmd = OCTEP_CTRL_NET_CMD_SET;
+       req.link.state = (up) ? OCTEP_CTRL_NET_STATE_UP : OCTEP_CTRL_NET_STATE_DOWN;
+
+       msg.hdr.flags = OCTEP_CTRL_MBOX_MSG_HDR_FLAG_REQ;
+       msg.hdr.sizew = OCTEP_CTRL_NET_H2F_STATE_REQ_SZW;
+       msg.msg = &req;
+       octep_ctrl_mbox_send(&oct->ctrl_mbox, &msg);
 }
 
 void octep_set_rx_state(struct octep_device *oct, bool up)
 {
+       struct octep_ctrl_net_h2f_req req = {};
+       struct octep_ctrl_mbox_msg msg = {};
+
+       req.hdr.cmd = OCTEP_CTRL_NET_H2F_CMD_RX_STATE;
+       req.link.cmd = OCTEP_CTRL_NET_CMD_SET;
+       req.link.state = (up) ? OCTEP_CTRL_NET_STATE_UP : OCTEP_CTRL_NET_STATE_DOWN;
+
+       msg.hdr.flags = OCTEP_CTRL_MBOX_MSG_HDR_FLAG_REQ;
+       msg.hdr.sizew = OCTEP_CTRL_NET_H2F_STATE_REQ_SZW;
+       msg.msg = &req;
+       octep_ctrl_mbox_send(&oct->ctrl_mbox, &msg);
 }
 
 int octep_get_mac_addr(struct octep_device *oct, u8 *addr)
 {
-       return -1;
+       struct octep_ctrl_net_h2f_req req = {};
+       struct octep_ctrl_net_h2f_resp *resp;
+       struct octep_ctrl_mbox_msg msg = {};
+       int err;
+
+       req.hdr.cmd = OCTEP_CTRL_NET_H2F_CMD_MAC;
+       req.link.cmd = OCTEP_CTRL_NET_CMD_GET;
+
+       msg.hdr.flags = OCTEP_CTRL_MBOX_MSG_HDR_FLAG_REQ;
+       msg.hdr.sizew = OCTEP_CTRL_NET_H2F_MAC_REQ_SZW;
+       msg.msg = &req;
+       err = octep_ctrl_mbox_send(&oct->ctrl_mbox, &msg);
+       if (err)
+               return err;
+
+       resp = (struct octep_ctrl_net_h2f_resp *)&req;
+       memcpy(addr, resp->mac.addr, ETH_ALEN);
+
+       return err;
 }
 
 int octep_get_link_info(struct octep_device *oct)
 {
-       return -1;
+       struct octep_ctrl_net_h2f_req req = {};
+       struct octep_ctrl_net_h2f_resp *resp;
+       struct octep_ctrl_mbox_msg msg = {};
+       int err;
+
+       req.hdr.cmd = OCTEP_CTRL_NET_H2F_CMD_LINK_INFO;
+       req.mac.cmd = OCTEP_CTRL_NET_CMD_GET;
+
+       msg.hdr.flags = OCTEP_CTRL_MBOX_MSG_HDR_FLAG_REQ;
+       msg.hdr.sizew = OCTEP_CTRL_NET_H2F_LINK_INFO_REQ_SZW;
+       msg.msg = &req;
+       err = octep_ctrl_mbox_send(&oct->ctrl_mbox, &msg);
+       if (err)
+               return err;
+
+       resp = (struct octep_ctrl_net_h2f_resp *)&req;
+       oct->link_info.supported_modes = resp->link_info.supported_modes;
+       oct->link_info.advertised_modes = resp->link_info.advertised_modes;
+       oct->link_info.autoneg = resp->link_info.autoneg;
+       oct->link_info.pause = resp->link_info.pause;
+       oct->link_info.speed = resp->link_info.speed;
+
+       return err;
 }
 
 int octep_set_link_info(struct octep_device *oct, struct octep_iface_link_info *link_info)
 {
-       return -1;
+       struct octep_ctrl_net_h2f_req req = {};
+       struct octep_ctrl_mbox_msg msg = {};
+
+       req.hdr.cmd = OCTEP_CTRL_NET_H2F_CMD_LINK_INFO;
+       req.link_info.cmd = OCTEP_CTRL_NET_CMD_SET;
+       req.link_info.info.advertised_modes = link_info->advertised_modes;
+       req.link_info.info.autoneg = link_info->autoneg;
+       req.link_info.info.pause = link_info->pause;
+       req.link_info.info.speed = link_info->speed;
+
+       msg.hdr.flags = OCTEP_CTRL_MBOX_MSG_HDR_FLAG_REQ;
+       msg.hdr.sizew = OCTEP_CTRL_NET_H2F_LINK_INFO_REQ_SZW;
+       msg.msg = &req;
+
+       return octep_ctrl_mbox_send(&oct->ctrl_mbox, &msg);
 }