Jerin Jacob 03cc9bd929 common/cnxk: add mailbox base infrastructure
This patch adds mailbox infra API's to communicate with Kernel AF
driver. These API's will be used by all the other cnxk drivers
for mbox init/fini, send/recv functionality.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Acked-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
2021-04-09 08:32:24 +02:00

484 lines
10 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(C) 2021 Marvell.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "roc_api.h"
#include "roc_priv.h"
#define RVU_AF_AFPF_MBOX0 (0x02000)
#define RVU_AF_AFPF_MBOX1 (0x02008)
#define RVU_PF_PFAF_MBOX0 (0xC00)
#define RVU_PF_PFAF_MBOX1 (0xC08)
#define RVU_PF_VFX_PFVF_MBOX0 (0x0000)
#define RVU_PF_VFX_PFVF_MBOX1 (0x0008)
#define RVU_VF_VFPF_MBOX0 (0x0000)
#define RVU_VF_VFPF_MBOX1 (0x0008)
/* RCLK, SCLK in MHz */
uint16_t dev_rclk_freq;
uint16_t dev_sclk_freq;
static inline uint16_t
msgs_offset(void)
{
return PLT_ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN);
}
void
mbox_fini(struct mbox *mbox)
{
mbox->reg_base = 0;
mbox->hwbase = 0;
plt_free(mbox->dev);
mbox->dev = NULL;
}
void
mbox_reset(struct mbox *mbox, int devid)
{
struct 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);
plt_spinlock_lock(&mdev->mbox_lock);
mdev->msg_size = 0;
mdev->rsp_size = 0;
tx_hdr->msg_size = 0;
tx_hdr->num_msgs = 0;
rx_hdr->msg_size = 0;
rx_hdr->num_msgs = 0;
plt_spinlock_unlock(&mdev->mbox_lock);
}
int
mbox_init(struct mbox *mbox, uintptr_t hwbase, uintptr_t reg_base,
int direction, int ndevs, uint64_t intr_offset)
{
struct mbox_dev *mdev;
char *var, *var_to;
int devid;
mbox->intr_offset = intr_offset;
mbox->reg_base = reg_base;
mbox->hwbase = hwbase;
switch (direction) {
case MBOX_DIR_AFPF:
case MBOX_DIR_PFVF:
mbox->tx_start = MBOX_DOWN_TX_START;
mbox->rx_start = MBOX_DOWN_RX_START;
mbox->tx_size = MBOX_DOWN_TX_SIZE;
mbox->rx_size = MBOX_DOWN_RX_SIZE;
break;
case MBOX_DIR_PFAF:
case MBOX_DIR_VFPF:
mbox->tx_start = MBOX_DOWN_RX_START;
mbox->rx_start = MBOX_DOWN_TX_START;
mbox->tx_size = MBOX_DOWN_RX_SIZE;
mbox->rx_size = MBOX_DOWN_TX_SIZE;
break;
case MBOX_DIR_AFPF_UP:
case MBOX_DIR_PFVF_UP:
mbox->tx_start = MBOX_UP_TX_START;
mbox->rx_start = MBOX_UP_RX_START;
mbox->tx_size = MBOX_UP_TX_SIZE;
mbox->rx_size = MBOX_UP_RX_SIZE;
break;
case MBOX_DIR_PFAF_UP:
case MBOX_DIR_VFPF_UP:
mbox->tx_start = MBOX_UP_RX_START;
mbox->rx_start = MBOX_UP_TX_START;
mbox->tx_size = MBOX_UP_RX_SIZE;
mbox->rx_size = MBOX_UP_TX_SIZE;
break;
default:
return -ENODEV;
}
switch (direction) {
case MBOX_DIR_AFPF:
case MBOX_DIR_AFPF_UP:
mbox->trigger = RVU_AF_AFPF_MBOX0;
mbox->tr_shift = 4;
break;
case MBOX_DIR_PFAF:
case MBOX_DIR_PFAF_UP:
mbox->trigger = RVU_PF_PFAF_MBOX1;
mbox->tr_shift = 0;
break;
case MBOX_DIR_PFVF:
case MBOX_DIR_PFVF_UP:
mbox->trigger = RVU_PF_VFX_PFVF_MBOX0;
mbox->tr_shift = 12;
break;
case MBOX_DIR_VFPF:
case MBOX_DIR_VFPF_UP:
mbox->trigger = RVU_VF_VFPF_MBOX1;
mbox->tr_shift = 0;
break;
default:
return -ENODEV;
}
mbox->dev = plt_zmalloc(ndevs * sizeof(struct mbox_dev), ROC_ALIGN);
if (!mbox->dev) {
mbox_fini(mbox);
return -ENOMEM;
}
mbox->ndevs = ndevs;
for (devid = 0; devid < ndevs; devid++) {
mdev = &mbox->dev[devid];
mdev->mbase = (void *)(mbox->hwbase + (devid * MBOX_SIZE));
plt_spinlock_init(&mdev->mbox_lock);
/* Init header to reset value */
mbox_reset(mbox, devid);
}
var = getenv("ROC_CN10K_MBOX_TIMEOUT");
var_to = getenv("ROC_MBOX_TIMEOUT");
if (var)
mbox->rsp_tmo = atoi(var);
else if (var_to)
mbox->rsp_tmo = atoi(var_to);
else
mbox->rsp_tmo = MBOX_RSP_TIMEOUT;
return 0;
}
/**
* @internal
* Allocate a message response
*/
struct mbox_msghdr *
mbox_alloc_msg_rsp(struct mbox *mbox, int devid, int size, int size_rsp)
{
struct mbox_dev *mdev = &mbox->dev[devid];
struct mbox_msghdr *msghdr = NULL;
plt_spinlock_lock(&mdev->mbox_lock);
size = PLT_ALIGN(size, MBOX_MSG_ALIGN);
size_rsp = PLT_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 */
mbox_memset(msghdr, 0, sizeof(*msghdr) + size);
/* Init message header with reset values */
msghdr->ver = MBOX_VERSION;
mdev->msg_size += size;
mdev->rsp_size += size_rsp;
msghdr->next_msgoff = mdev->msg_size + msgs_offset();
exit:
plt_spinlock_unlock(&mdev->mbox_lock);
return msghdr;
}
/**
* @internal
* Send a mailbox message
*/
void
mbox_msg_send(struct mbox *mbox, int devid)
{
struct 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 */
plt_wmb();
/* The interrupt should be fired after num_msgs is written
* to the shared memory
*/
plt_write64(1, (volatile void *)(mbox->reg_base +
(mbox->trigger |
(devid << mbox->tr_shift))));
}
/**
* @internal
* Wait and get mailbox response
*/
int
mbox_get_rsp(struct mbox *mbox, int devid, void **msg)
{
struct mbox_dev *mdev = &mbox->dev[devid];
struct mbox_msghdr *msghdr;
uint64_t offset;
int rc;
rc = mbox_wait_for_rsp(mbox, devid);
if (rc < 0)
return -EIO;
plt_rmb();
offset = mbox->rx_start +
PLT_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 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 = plt_read64(reg_addr);
if (timeout >= wait_us)
return -ETIMEDOUT;
plt_delay_us(sleep);
timeout += sleep;
} while (!rsp_reg);
plt_rmb();
/* Clear interrupt */
plt_write64(rsp_reg, reg_addr);
/* Reset mbox */
mbox_reset(mbox, 0);
return 0;
}
/**
* @internal
* Wait and get mailbox response with timeout
*/
int
mbox_get_rsp_tmo(struct mbox *mbox, int devid, void **msg, uint32_t tmo)
{
struct mbox_dev *mdev = &mbox->dev[devid];
struct mbox_msghdr *msghdr;
uint64_t offset;
int rc;
rc = mbox_wait_for_rsp_tmo(mbox, devid, tmo);
if (rc != 1)
return -EIO;
plt_rmb();
offset = mbox->rx_start +
PLT_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 mbox *mbox, int devid, uint32_t rst_timo)
{
volatile struct 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) {
plt_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);
plt_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;
}
plt_rmb();
}
return 0;
}
int
mbox_wait_for_rsp_tmo(struct mbox *mbox, int devid, uint32_t tmo)
{
struct mbox_dev *mdev = &mbox->dev[devid];
int rc = 0;
/* Sync with mbox region */
plt_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 (plt_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
mbox_wait_for_rsp(struct mbox *mbox, int devid)
{
return mbox_wait_for_rsp_tmo(mbox, devid, mbox->rsp_tmo);
}
int
mbox_get_availmem(struct mbox *mbox, int devid)
{
struct mbox_dev *mdev = &mbox->dev[devid];
int avail;
plt_spinlock_lock(&mdev->mbox_lock);
avail = mbox->tx_size - mdev->msg_size - msgs_offset();
plt_spinlock_unlock(&mdev->mbox_lock);
return avail;
}
int
send_ready_msg(struct mbox *mbox, uint16_t *pcifunc)
{
struct ready_msg_rsp *rsp;
int rc;
mbox_alloc_msg_ready(mbox);
rc = mbox_process_msg(mbox, (void *)&rsp);
if (rc)
return rc;
if (rsp->hdr.ver != MBOX_VERSION) {
plt_err("Incompatible MBox versions(AF: 0x%04x Client: 0x%04x)",
rsp->hdr.ver, MBOX_VERSION);
return -EPIPE;
}
if (pcifunc)
*pcifunc = rsp->hdr.pcifunc;
/* Save rclk & sclk freq */
if (!dev_rclk_freq || !dev_sclk_freq) {
dev_rclk_freq = rsp->rclk_freq;
dev_sclk_freq = rsp->sclk_freq;
}
return 0;
}
int
reply_invalid_msg(struct mbox *mbox, int devid, uint16_t pcifunc, uint16_t id)
{
struct msg_rsp *rsp;
rsp = (struct msg_rsp *)mbox_alloc_msg(mbox, devid, sizeof(*rsp));
if (!rsp)
return -ENOMEM;
rsp->hdr.id = id;
rsp->hdr.sig = MBOX_RSP_SIG;
rsp->hdr.rc = MBOX_MSG_INVALID;
rsp->hdr.pcifunc = pcifunc;
return 0;
}
/**
* @internal
* Convert mail box ID to name
*/
const char *
mbox_id2name(uint16_t id)
{
switch (id) {
default:
return "INVALID ID";
#define M(_name, _id, _1, _2, _3) \
case _id: \
return #_name;
MBOX_MESSAGES
MBOX_UP_CGX_MESSAGES
#undef M
}
}
int
mbox_id2size(uint16_t id)
{
switch (id) {
default:
return 0;
#define M(_1, _id, _2, _req_type, _3) \
case _id: \
return sizeof(struct _req_type);
MBOX_MESSAGES
MBOX_UP_CGX_MESSAGES
#undef M
}
}