// SPDX-License-Identifier: GPL-2.0 /* Marvell Octeon EP (EndPoint) Ethernet Driver * * Copyright (C) 2020 Marvell. * */ #include #include #include #include #include #include #include #include #include #include #include "octep_ctrl_mbox.h" #include "octep_config.h" #include "octep_main.h" /* Timeout in msecs for message response */ #define OCTEP_CTRL_MBOX_MSG_TIMEOUT_MS 100 /* Time in msecs to wait for message response */ #define OCTEP_CTRL_MBOX_MSG_WAIT_MS 10 #define OCTEP_CTRL_MBOX_INFO_MAGIC_NUM_OFFSET(m) (m) #define OCTEP_CTRL_MBOX_INFO_BARMEM_SZ_OFFSET(m) ((m) + 8) #define OCTEP_CTRL_MBOX_INFO_HOST_STATUS_OFFSET(m) ((m) + 24) #define OCTEP_CTRL_MBOX_INFO_FW_STATUS_OFFSET(m) ((m) + 144) #define OCTEP_CTRL_MBOX_H2FQ_INFO_OFFSET(m) ((m) + OCTEP_CTRL_MBOX_INFO_SZ) #define OCTEP_CTRL_MBOX_H2FQ_PROD_OFFSET(m) (OCTEP_CTRL_MBOX_H2FQ_INFO_OFFSET(m)) #define OCTEP_CTRL_MBOX_H2FQ_CONS_OFFSET(m) ((OCTEP_CTRL_MBOX_H2FQ_INFO_OFFSET(m)) + 4) #define OCTEP_CTRL_MBOX_H2FQ_ELEM_SZ_OFFSET(m) ((OCTEP_CTRL_MBOX_H2FQ_INFO_OFFSET(m)) + 8) #define OCTEP_CTRL_MBOX_H2FQ_ELEM_CNT_OFFSET(m) ((OCTEP_CTRL_MBOX_H2FQ_INFO_OFFSET(m)) + 12) #define OCTEP_CTRL_MBOX_F2HQ_INFO_OFFSET(m) ((m) + \ OCTEP_CTRL_MBOX_INFO_SZ + \ OCTEP_CTRL_MBOX_H2FQ_INFO_SZ) #define OCTEP_CTRL_MBOX_F2HQ_PROD_OFFSET(m) (OCTEP_CTRL_MBOX_F2HQ_INFO_OFFSET(m)) #define OCTEP_CTRL_MBOX_F2HQ_CONS_OFFSET(m) ((OCTEP_CTRL_MBOX_F2HQ_INFO_OFFSET(m)) + 4) #define OCTEP_CTRL_MBOX_F2HQ_ELEM_SZ_OFFSET(m) ((OCTEP_CTRL_MBOX_F2HQ_INFO_OFFSET(m)) + 8) #define OCTEP_CTRL_MBOX_F2HQ_ELEM_CNT_OFFSET(m) ((OCTEP_CTRL_MBOX_F2HQ_INFO_OFFSET(m)) + 12) #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) { u64 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; } 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(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) { 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) { 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)); pr_info("Octep ctrl mbox : Uninit successful.\n"); return 0; }