246 lines
7.0 KiB
C
246 lines
7.0 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/* Marvell Octeon EP (EndPoint) Ethernet Driver
|
||
|
*
|
||
|
* Copyright (C) 2020 Marvell.
|
||
|
*
|
||
|
*/
|
||
|
#include <linux/types.h>
|
||
|
#include <linux/errno.h>
|
||
|
#include <linux/string.h>
|
||
|
#include <linux/mutex.h>
|
||
|
#include <linux/jiffies.h>
|
||
|
#include <linux/sched.h>
|
||
|
#include <linux/sched/signal.h>
|
||
|
#include <linux/io.h>
|
||
|
#include <linux/pci.h>
|
||
|
#include <linux/etherdevice.h>
|
||
|
|
||
|
#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;
|
||
|
}
|