Skip to content

Commit

Permalink
octeon_ep: add separate mailbox command and response queues
Browse files Browse the repository at this point in the history
Enhance control mailbox protocol to support following
 - separate command and response queues
    * command queue to send control commands to firmware.
    * response queue to receive responses and notifications from
      firmware.
 - variable size messages using scatter/gather

Signed-off-by: Abhijit Ayarekar <aayarekar@marvell.com>
Signed-off-by: Veerasenareddy Burru <vburru@marvell.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Veerasenareddy Burru authored and davem330 committed Mar 27, 2023
1 parent 7c05d3d commit 577f0d1
Show file tree
Hide file tree
Showing 6 changed files with 529 additions and 411 deletions.
276 changes: 150 additions & 126 deletions drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,41 +24,49 @@
/* 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)
/* Size of mbox info in bytes */
#define OCTEP_CTRL_MBOX_INFO_SZ 256
/* Size of mbox host to fw queue info in bytes */
#define OCTEP_CTRL_MBOX_H2FQ_INFO_SZ 16
/* Size of mbox fw to host queue info in bytes */
#define OCTEP_CTRL_MBOX_F2HQ_INFO_SZ 16

#define OCTEP_CTRL_MBOX_TOTAL_INFO_SZ (OCTEP_CTRL_MBOX_INFO_SZ + \
OCTEP_CTRL_MBOX_H2FQ_INFO_SZ + \
OCTEP_CTRL_MBOX_F2HQ_INFO_SZ)

#define OCTEP_CTRL_MBOX_INFO_MAGIC_NUM(m) (m)
#define OCTEP_CTRL_MBOX_INFO_BARMEM_SZ(m) ((m) + 8)
#define OCTEP_CTRL_MBOX_INFO_HOST_STATUS(m) ((m) + 24)
#define OCTEP_CTRL_MBOX_INFO_FW_STATUS(m) ((m) + 144)

#define OCTEP_CTRL_MBOX_H2FQ_INFO(m) ((m) + OCTEP_CTRL_MBOX_INFO_SZ)
#define OCTEP_CTRL_MBOX_H2FQ_PROD(m) (OCTEP_CTRL_MBOX_H2FQ_INFO(m))
#define OCTEP_CTRL_MBOX_H2FQ_CONS(m) ((OCTEP_CTRL_MBOX_H2FQ_INFO(m)) + 4)
#define OCTEP_CTRL_MBOX_H2FQ_SZ(m) ((OCTEP_CTRL_MBOX_H2FQ_INFO(m)) + 8)

#define OCTEP_CTRL_MBOX_F2HQ_INFO(m) ((m) + \
OCTEP_CTRL_MBOX_INFO_SZ + \
OCTEP_CTRL_MBOX_H2FQ_INFO_SZ)
#define OCTEP_CTRL_MBOX_F2HQ_PROD(m) (OCTEP_CTRL_MBOX_F2HQ_INFO(m))
#define OCTEP_CTRL_MBOX_F2HQ_CONS(m) ((OCTEP_CTRL_MBOX_F2HQ_INFO(m)) + 4)
#define OCTEP_CTRL_MBOX_F2HQ_SZ(m) ((OCTEP_CTRL_MBOX_F2HQ_INFO(m)) + 8)

static const u32 mbox_hdr_sz = sizeof(union octep_ctrl_mbox_msg_hdr);

static u32 octep_ctrl_mbox_circq_inc(u32 index, u32 inc, u32 sz)
{
return (index + 1) & mask;
return (index + inc) % sz;
}

static u32 octep_ctrl_mbox_circq_space(u32 pi, u32 ci, u32 mask)
static u32 octep_ctrl_mbox_circq_space(u32 pi, u32 ci, u32 sz)
{
return mask - ((pi - ci) & mask);
return sz - (abs(pi - ci) % sz);
}

static u32 octep_ctrl_mbox_circq_depth(u32 pi, u32 ci, u32 mask)
static u32 octep_ctrl_mbox_circq_depth(u32 pi, u32 ci, u32 sz)
{
return ((pi - ci) & mask);
return (abs(pi - ci) % sz);
}

int octep_ctrl_mbox_init(struct octep_ctrl_mbox *mbox)
Expand All @@ -73,172 +81,188 @@ int octep_ctrl_mbox_init(struct octep_ctrl_mbox *mbox)
return -EINVAL;
}

magic_num = readq(OCTEP_CTRL_MBOX_INFO_MAGIC_NUM_OFFSET(mbox->barmem));
magic_num = readq(OCTEP_CTRL_MBOX_INFO_MAGIC_NUM(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));
status = readq(OCTEP_CTRL_MBOX_INFO_FW_STATUS(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));
mbox->barmem_sz = readl(OCTEP_CTRL_MBOX_INFO_BARMEM_SZ(mbox->barmem));

writeq(OCTEP_CTRL_MBOX_STATUS_INIT, OCTEP_CTRL_MBOX_INFO_HOST_STATUS_OFFSET(mbox->barmem));
writeq(OCTEP_CTRL_MBOX_STATUS_INIT,
OCTEP_CTRL_MBOX_INFO_HOST_STATUS(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->h2fq.sz = readl(OCTEP_CTRL_MBOX_H2FQ_SZ(mbox->barmem));
mbox->h2fq.hw_prod = OCTEP_CTRL_MBOX_H2FQ_PROD(mbox->barmem);
mbox->h2fq.hw_cons = OCTEP_CTRL_MBOX_H2FQ_CONS(mbox->barmem);
mbox->h2fq.hw_q = mbox->barmem + OCTEP_CTRL_MBOX_TOTAL_INFO_SZ;

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);
mbox->f2hq.sz = readl(OCTEP_CTRL_MBOX_F2HQ_SZ(mbox->barmem));
mbox->f2hq.hw_prod = OCTEP_CTRL_MBOX_F2HQ_PROD(mbox->barmem);
mbox->f2hq.hw_cons = OCTEP_CTRL_MBOX_F2HQ_CONS(mbox->barmem);
mbox->f2hq.hw_q = mbox->barmem +
OCTEP_CTRL_MBOX_TOTAL_INFO_SZ +
mbox->h2fq.sz;

/* 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));
writeq(OCTEP_CTRL_MBOX_STATUS_READY,
OCTEP_CTRL_MBOX_INFO_HOST_STATUS(mbox->barmem));

pr_info("Octep ctrl mbox : Init successful.\n");

return 0;
}

static void
octep_write_mbox_data(struct octep_ctrl_mbox_q *q, u32 *pi, u32 ci, void *buf, u32 w_sz)
{
u8 __iomem *qbuf;
u32 cp_sz;

/* Assumption: Caller has ensured enough write space */
qbuf = (q->hw_q + *pi);
if (*pi < ci) {
/* copy entire w_sz */
memcpy_toio(qbuf, buf, w_sz);
*pi = octep_ctrl_mbox_circq_inc(*pi, w_sz, q->sz);
} else {
/* copy up to end of queue */
cp_sz = min((q->sz - *pi), w_sz);
memcpy_toio(qbuf, buf, cp_sz);
w_sz -= cp_sz;
*pi = octep_ctrl_mbox_circq_inc(*pi, cp_sz, q->sz);
if (w_sz) {
/* roll over and copy remaining w_sz */
buf += cp_sz;
qbuf = (q->hw_q + *pi);
memcpy_toio(qbuf, buf, w_sz);
*pi = octep_ctrl_mbox_circq_inc(*pi, w_sz, q->sz);
}
}
}

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_msg_buf *sg;
struct octep_ctrl_mbox_q *q;
unsigned long expire;
u64 *mbuf, *word0;
u8 __iomem *qidx;
u16 pi, ci;
int i;
u32 pi, ci, buf_sz, w_sz;
int s;

if (!mbox || !msg)
return -EINVAL;

if (readq(OCTEP_CTRL_MBOX_INFO_FW_STATUS(mbox->barmem)) != OCTEP_CTRL_MBOX_STATUS_READY)
return -EIO;

mutex_lock(&mbox->h2fq_lock);
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);
if (octep_ctrl_mbox_circq_space(pi, ci, q->sz) < (msg->hdr.s.sz + mbox_hdr_sz)) {
mutex_unlock(&mbox->f2hq_lock);
return -EAGAIN;
}

pi = octep_ctrl_mbox_circq_inc(pi, q->mask);
octep_write_mbox_data(q, &pi, ci, (void *)&msg->hdr, mbox_hdr_sz);
buf_sz = msg->hdr.s.sz;
for (s = 0; ((s < msg->sg_num) && (buf_sz > 0)); s++) {
sg = &msg->sg_list[s];
w_sz = (sg->sz <= buf_sz) ? sg->sz : buf_sz;
octep_write_mbox_data(q, &pi, ci, sg->msg, w_sz);
buf_sz -= w_sz;
}
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;
return 0;
}

static void
octep_read_mbox_data(struct octep_ctrl_mbox_q *q, u32 pi, u32 *ci, void *buf, u32 r_sz)
{
u8 __iomem *qbuf;
u32 cp_sz;

/* Assumption: Caller has ensured enough read space */
qbuf = (q->hw_q + *ci);
if (*ci < pi) {
/* copy entire r_sz */
memcpy_fromio(buf, qbuf, r_sz);
*ci = octep_ctrl_mbox_circq_inc(*ci, r_sz, q->sz);
} else {
/* copy up to end of queue */
cp_sz = min((q->sz - *ci), r_sz);
memcpy_fromio(buf, qbuf, cp_sz);
r_sz -= cp_sz;
*ci = octep_ctrl_mbox_circq_inc(*ci, cp_sz, q->sz);
if (r_sz) {
/* roll over and copy remaining r_sz */
buf += cp_sz;
qbuf = (q->hw_q + *ci);
memcpy_fromio(buf, qbuf, r_sz);
*ci = octep_ctrl_mbox_circq_inc(*ci, r_sz, q->sz);
}
}
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_msg_buf *sg;
u32 pi, ci, r_sz, buf_sz, q_depth;
struct octep_ctrl_mbox_q *q;
u32 count, pi, ci;
u8 __iomem *qidx;
u64 *mbuf;
int i;
int s;

if (!mbox || !msg)
return -EINVAL;
if (readq(OCTEP_CTRL_MBOX_INFO_FW_STATUS(mbox->barmem)) != OCTEP_CTRL_MBOX_STATUS_READY)
return -EIO;

mutex_lock(&mbox->f2hq_lock);
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));
q_depth = octep_ctrl_mbox_circq_depth(pi, ci, q->sz);
if (q_depth < mbox_hdr_sz) {
mutex_unlock(&mbox->f2hq_lock);
return -EAGAIN;
}

ci = octep_ctrl_mbox_circq_inc(ci, q->mask);
octep_read_mbox_data(q, pi, &ci, (void *)&msg->hdr, mbox_hdr_sz);
buf_sz = msg->hdr.s.sz;
for (s = 0; ((s < msg->sg_num) && (buf_sz > 0)); s++) {
sg = &msg->sg_list[s];
r_sz = (sg->sz <= buf_sz) ? sg->sz : buf_sz;
octep_read_mbox_data(q, pi, &ci, sg->msg, r_sz);
buf_sz -= r_sz;
}
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;
if (!mbox->barmem)
return -EINVAL;

writeq(OCTEP_CTRL_MBOX_STATUS_UNINIT,
OCTEP_CTRL_MBOX_INFO_HOST_STATUS_OFFSET(mbox->barmem));
writeq(OCTEP_CTRL_MBOX_STATUS_INVALID,
OCTEP_CTRL_MBOX_INFO_HOST_STATUS(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;
Expand Down

0 comments on commit 577f0d1

Please sign in to comment.