mailbox and remoteproc

This commit is contained in:
Justin Hammond 2023-02-11 20:11:34 +08:00
parent ea57961469
commit ec62e8fc7b
6 changed files with 125 additions and 67 deletions

View file

@ -18,8 +18,8 @@
chosen {
stdout-path = "serial0:2000000n8";
bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=/dev/mmcblk0p2 rootwait rootfstype=ext4 dyndbg=\"file bl808_remoteproc.c +p; module bflb_ipc +p; file mailbox.c +p; file rpmsg-net.c +p;\"";
//bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=/dev/mmcblk0p2 rootwait rootfstype=ext4 dyndbg=\"file rpmsg-net.c +p;\"";
//bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=/dev/mmcblk0p2 rootwait rootfstype=ext4 dyndbg=\"file bl808_remoteproc.c +p; module bflb_ipc +p; file mailbox.c +p; file rpmsg-net.c +p;\"";
bootargs = "console=ttyS0,2000000 loglevel=8 earlycon=sbi root=/dev/mmcblk0p2 rootwait rootfstype=ext4 dyndbg=\"file rpmsg-net.c +p;\"";
linux,initrd-start = <0x0 0x52000000>;
linux,initrd-end = <0x0 0x52941784>;
};

View file

@ -172,7 +172,7 @@
};
rproc: rproc@22054000 {
compatible = "bflb,bl808-rproc";
compatible = "bflb,bflb-rproc";
reg = <0x22054000 0x4000>;
memory-region = <&vdev0vring0>, <&vdev0vring1>, <&vdev0buffer>;
mboxes = <&ipclic BFLB_IPC_TARGET_M0 BFLB_IPC_MBOX_VIRTIO BFLB_IPC_MBOX_VIRTIO_OP_KICK>,

View file

@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/bflb-mailbox.h>
#include <dt-bindings/mailbox/bflb-ipc.h>
/* IPC Register offsets */
@ -65,9 +66,8 @@ static inline struct bflb_ipc *to_bflb_ipc(struct mbox_controller *mboxctlr)
static inline u32 bflb_ipc_get_hwirq(u16 source, u16 device)
{
pr_debug("%s: source: %u, device: %u\n", __func__, source, device);
// dev_dbg("%s: source: %u, device: %u\n", __func__, source, device);
return device;
}
@ -76,7 +76,8 @@ static inline u32 bflb_ipc_get_hwirq(u16 source, u16 device)
static void bflb_ipc_dump_regs(struct bflb_ipc *ipc)
{
int i;
for (i=0; i<4; i++) {
for (i = 0; i < 4; i++) {
dev_dbg(ipc->dev, "base %px %d\n", ipc->base[i], i);
dev_dbg(ipc->dev, "ISWR: 0x%08x\n", readl(ipc->base[i] + IPC_REG_ISWR));
dev_dbg(ipc->dev, "IRSRR: 0x%08x\n", readl(ipc->base[i] + IPC_REG_IRSRR));
@ -90,7 +91,8 @@ static void bflb_ipc_dump_regs(struct bflb_ipc *ipc)
}
#endif
struct mbox_chan *bflb_mbox_find_chan(struct bflb_ipc *ipc, struct bflb_ipc_chan_info *chaninfo) {
struct mbox_chan *bflb_mbox_find_chan(struct bflb_ipc *ipc, struct bflb_ipc_chan_info *chaninfo)
{
struct bflb_ipc_chan_info *mchan;
struct mbox_controller *mboxctlr = &ipc->mboxctlr;
struct mbox_chan *chan;
@ -99,8 +101,6 @@ struct mbox_chan *bflb_mbox_find_chan(struct bflb_ipc *ipc, struct bflb_ipc_chan
dev = ipc->dev;
//dev_dbg(dev, "%s Find Chan %d %d %d\n", __func__, chaninfo->cpu_id, chaninfo->service_id, chaninfo->op_id);
for (chan_id = 0; chan_id < mboxctlr->num_chans; chan_id++) {
chan = &ipc->chans[chan_id];
mchan = chan->con_priv;
@ -112,7 +112,7 @@ struct mbox_chan *bflb_mbox_find_chan(struct bflb_ipc *ipc, struct bflb_ipc_chan
mchan->op_id == chaninfo->op_id)
return chan;
}
dev_err(dev, "%s: No channel found for cpu_id %d service_id %d op_id %d",
dev_err(dev, "%s: No channel found for cpu_id %d service_id %d op_id %d",
__func__, chaninfo->cpu_id, chaninfo->service_id, chaninfo->op_id);
return ERR_PTR(-EINVAL);
@ -121,42 +121,38 @@ struct mbox_chan *bflb_mbox_find_chan(struct bflb_ipc *ipc, struct bflb_ipc_chan
/* called when we get a RX interrupt from another processor
* this indicates there is a message waitinf for us in
* IPC_REG_ILSHR and IPC_REG_ILSLR registers to process
* IPC_REG_ILSHR and IPC_REG_ILSLR registers to process
*/
static void bflb_mbox_rx_irq_fn(int from_cpu, struct bflb_ipc *ipc)
{
struct mbox_chan *chan;
struct bflb_ipc_chan_info bflbchan;
struct bflb_mbox_msg msg;
u32 sig_op;
/* update this when we support LP */
u32 sig_op = readl(ipc->base[1] + IPC_REG_ILSHR);
u32 param = readl(ipc->base[1] + IPC_REG_ILSLR);
sig_op = readl(ipc->base[1] + IPC_REG_ILSHR);
msg.param = readl(ipc->base[1] + IPC_REG_ILSLR);
WARN_ON(sig_op == 0);
bflbchan.cpu_id = from_cpu;
bflbchan.service_id = (sig_op >> 16) & 0xFFFF;
bflbchan.op_id = sig_op & 0xFFFF;
chan = bflb_mbox_find_chan(ipc, &bflbchan);
if (IS_ERR(chan)) {
dev_err(ipc->dev, "no channel for signal cpu_id: %d service: %d op: %d\r\n", bflbchan.cpu_id, bflbchan.service_id, bflbchan.op_id);
return;
}
dev_info(ipc->dev, "Got MBOX Signal cpu: %d service %d op %d param %x\r\n", bflbchan.cpu_id, bflbchan.service_id, bflbchan.op_id, param);
dev_dbg(ipc->dev, "Got MBOX Signal cpu: %d service %d op %d param %x\r\n", bflbchan.cpu_id, bflbchan.service_id, bflbchan.op_id, msg.param);
mbox_chan_received_data(chan, param);
dev_info(ipc->dev, "bflb_mbox_rx\r\n");
return;
mbox_chan_received_data(chan, &msg);
}
/* called when we get a interupt back on our TX IRQ.
* This is usualy a EOI interupt
* This is a EOI interupt
*/
static void bflb_mbox_tx_irq_fn(u8 from_cpu, struct bflb_ipc *ipc)
{
@ -164,6 +160,7 @@ static void bflb_mbox_tx_irq_fn(u8 from_cpu, struct bflb_ipc *ipc)
struct bflb_ipc_chan_info bflbchan;
u32 sig_op = readl(ipc->base[2] + IPC_REG_ILSHR);
u32 param = readl(ipc->base[2] + IPC_REG_ILSLR);
bflbchan.cpu_id = from_cpu;
bflbchan.service_id = (sig_op >> 16) & 0xFFFF;
@ -171,15 +168,17 @@ static void bflb_mbox_tx_irq_fn(u8 from_cpu, struct bflb_ipc *ipc)
chan = bflb_mbox_find_chan(ipc, &bflbchan);
if (IS_ERR(chan)) {
dev_err(ipc->dev, "no channel for EOI signal cpu_id: %d service: %d op: %d\r\n", bflbchan.cpu_id, bflbchan.service_id, bflbchan.op_id);
dev_err(ipc->dev, "no channel for EOI signal cpu_id: %d service: %d op: %d Param: %d $$$$$$$$$", bflbchan.cpu_id, bflbchan.service_id, bflbchan.op_id, param);
return;
}
dev_info(ipc->dev, "Got MBOX EOI Signal cpu: %d service %d op %d\r\n", bflbchan.cpu_id, bflbchan.service_id, bflbchan.op_id);
dev_dbg(ipc->dev, "Got MBOX EOI Signal cpu: %d service %d op %d Param: %d $$$$$$$$$$$$$$$$$$$$", bflbchan.cpu_id, bflbchan.service_id, bflbchan.op_id, param);
/* clear the IPC_REG_ILSLR and IPC_REG_ILSHR */
writel(0, ipc->base[2] + IPC_REG_ILSLR);
writel(0, ipc->base[2] + IPC_REG_ILSHR);
mbox_chan_txdone(chan, 0);
return;
}
static irqreturn_t bflb_ipc_irq_fn(int irq, void *data)
@ -196,7 +195,7 @@ static irqreturn_t bflb_ipc_irq_fn(int irq, void *data)
else if (pos == BFLB_IPC_DEVICE_MBOX_TX)
/* we use target here as its a EOI from a send */
bflb_mbox_tx_irq_fn(BFLB_IPC_TARGET_M0, ipc);
else
else
generic_handle_domain_irq(ipc->irq_domain, pos);
}
writel(stat, ipc->base[1] + IPC_REG_ICR);
@ -263,47 +262,61 @@ static const struct irq_domain_ops bflb_ipc_irq_ops = {
.xlate = bflb_ipc_domain_xlate,
};
/* JH: Figure out if M0 has processed the last signal sent
* by checking if the High/Low registers are cleared
#if 0
/* Shouldn't actually be fail as we clear the High/Low registers in a EOI
* but this protects if we screw up our mailbox handling
*/
static bool bflb_ipc_mbox_can_send(struct mbox_chan *chan)
{
struct bflb_ipc *ipc = to_bflb_ipc(chan->mbox);
u32 mbox_high = readl(ipc->base[2] + IPC_REG_ILSHR);
/* check the low register first as we clear that last in our EOI, so this
* should protected to a limited extent
*/
u32 mbox_low = readl(ipc->base[2] + IPC_REG_ILSLR);
u32 mbox_high = readl(ipc->base[2] + IPC_REG_ILSHR);
dev_dbg(ipc->dev, "%s: low: 0x%08x high: 0x%08x\r\n", __func__, mbox_low, mbox_high);
if (mbox_low | mbox_high)
dev_warn_ratelimited(ipc->dev, "%s: low: 0x%08x high: 0x%08x\r\n", __func__, mbox_low, mbox_high);
writel(0, ipc->base[2] + IPC_REG_ILSLR);
writel(0, ipc->base[2] + IPC_REG_ILSHR);
return !(mbox_low | mbox_high);
}
#endif
static int bflb_ipc_mbox_send_data(struct mbox_chan *chan, void *data)
{
struct bflb_ipc *ipc = to_bflb_ipc(chan->mbox);
struct bflb_ipc_chan_info *mchan = chan->con_priv;
int *param = data;
struct bflb_mbox_msg *msg = data;
u32 tmpVal = (mchan->service_id << 16) | (mchan->op_id & 0xFFFF);
dev_dbg(ipc->dev, "%s: cpu: %d singal: %d op: %d (0x%x) param: %d\n", __func__, mchan->cpu_id, mchan->service_id, mchan->op_id, tmpVal, *param);
#if 0
if (!bflb_ipc_mbox_can_send(chan))
return -EBUSY;
#endif
dev_dbg(ipc->dev, "%s %d: cpu: %d singal: %d op: %d (0x%x) param: %d", __func__, msg->id, mchan->cpu_id, mchan->service_id, mchan->op_id, tmpVal, msg->param);
// /* write our signal number to high register */
writel(tmpVal, ipc->base[2] + IPC_REG_ILSHR);
// /* write our data to low register */
writel(*param, ipc->base[2] + IPC_REG_ILSLR);
writel(msg->param, ipc->base[2] + IPC_REG_ILSLR);
/* and now kick the remote processor */
writel((1 << BFLB_IPC_DEVICE_MBOX_TX), ipc->base[2] + IPC_REG_ISWR);
dev_dbg(ipc->dev, "%s: done\r\n", __func__);
dev_dbg(ipc->dev, "%s %d: done param: %d", __func__, msg->id, msg->param);
return 0;
}
static void bflb_ipc_mbox_shutdown(struct mbox_chan *chan)
{
pr_debug("%s\n", __func__);
struct bflb_ipc *ipc = to_bflb_ipc(chan->mbox);
dev_dbg(ipc->dev, "%s\n", __func__);
chan->con_priv = NULL;
}
@ -320,7 +333,7 @@ static struct mbox_chan *bflb_ipc_mbox_xlate(struct mbox_controller *mboxctlr,
dev_dbg(dev, "%s\n", __func__);
if (ph->args_count != 3) {
pr_debug("invalid number of arguments");
dev_err(dev, "invalid number of arguments");
return ERR_PTR(-EINVAL);
}
@ -333,28 +346,27 @@ static struct mbox_chan *bflb_ipc_mbox_xlate(struct mbox_controller *mboxctlr,
else if (mchan->cpu_id == ph->args[0] &&
mchan->service_id == ph->args[1] &&
mchan->op_id == ph->args[2]) {
pr_debug("channel already in use %d %d %d", ph->args[0], ph->args[1], ph->args[2]);
dev_err(dev, "channel already in use %d %d %d", ph->args[0], ph->args[1], ph->args[2]);
return ERR_PTR(-EBUSY);
}
}
if (chan_id >= mboxctlr->num_chans) {
pr_debug("no free channels");
dev_err(dev, "no free channels");
return ERR_PTR(-EBUSY);
}
mchan = devm_kzalloc(dev, sizeof(*mchan), GFP_KERNEL);
if (!mchan) {
pr_debug("no memory for channel");
if (!mchan)
return ERR_PTR(-ENOMEM);
}
mchan->cpu_id = ph->args[0];
mchan->service_id = ph->args[1];
mchan->op_id = ph->args[2];
chan->con_priv = mchan;
dev_dbg(dev, "%s mbox %s: %d cpu: %d service: %d op: %d", __func__, ph->np->full_name, chan_id, mchan->cpu_id, mchan->service_id, mchan->op_id);
return chan;
}
@ -445,7 +457,7 @@ static int bflb_ipc_probe(struct platform_device *pdev)
ipc->dev = &pdev->dev;
for (i=0; i<4; i++) {
for (i = 0; i < 4; i++) {
ipc->base[i] = devm_platform_ioremap_resource(pdev, i);
if (IS_ERR(ipc->base[i]))
return PTR_ERR(ipc->base[i]);

View file

@ -18,7 +18,9 @@
#include <linux/platform_device.h>
#include <linux/remoteproc.h>
#include <linux/mailbox_client.h>
#include <linux/mailbox_controller.h>
#include <linux/delay.h>
#include <linux/bflb-mailbox.h>
#include "remoteproc_internal.h"
/**
@ -232,8 +234,6 @@ static int bflb_rproc_stop(struct rproc *rproc)
return 0;
}
#include <linux/mailbox_controller.h>
/* kick the virtqueue to let M0 know there is a update to the vring */
static void bflb_rproc_send_kick(struct rproc *rproc, int vqid)
{
@ -241,27 +241,51 @@ static void bflb_rproc_send_kick(struct rproc *rproc, int vqid)
struct bflb_rproc *drproc = (struct bflb_rproc *)rproc->priv;
struct bflb_mbox *mb = &drproc->mbox_tx;
struct mbox_chan *chan = mb->chan;
struct bflb_mbox_msg *msg;
int ret;
int i;
static int count;
/* just for debugging atm */
count++;
msg = kzalloc(sizeof(struct bflb_mbox_msg), GFP_KERNEL);
if (!msg)
return;
/* we need a small delay before kicking the other side
* (I assume to allow the ring to update/flush etc?)
* without this, we get lots of "empty ring" messages on the
* other side
*/
mdelay(1);
msg->param = vqid;
msg->id = count;
/* Kick the other CPU to let it know the vrings are updated */
dev_info(dev, "%s Mailbox: %s %d", __func__, mb->name, vqid);
ret = mbox_send_message(chan, &vqid);
if (!ret) {
dev_err(dev, "%s Mailbox %s sending %d Failed: %d", __func__, mb->name, vqid, ret);
} else {
dev_info(dev, "%s Mailbox %s done %d size: %d free: %d: %d", __func__, mb->name, vqid, chan->msg_count, chan->msg_free, ret);
dev_dbg(dev, "%s %d Mailbox: %s %d", __func__, msg->id, mb->name, msg->param);
/* we occasionally get a EOI timeout, so retry upto 3 times */
for (i = 0; i < 3; i++) {
ret = mbox_send_message(chan, msg);
if (ret >= 0)
goto done;
dev_warn(dev, "%s %d Mailbox %s sending %d Failed: %d - Retrying %d", __func__, msg->id, mb->name, msg->param, ret, i);
}
done:
dev_dbg(dev, "%s %d Mailbox %s done %d: %d", __func__, msg->id, mb->name, msg->param, ret);
kfree(msg);
}
static void bflb_rproc_recv_kick(struct work_struct *work)
{
struct bflb_mbox *mb = container_of(work, struct bflb_mbox, vq_work);
struct rproc *rproc = dev_get_drvdata(mb->client.dev);
dev_info(rproc->dev.parent, "%s mailbox: %s: %d", __func__, mb->name, mb->vq_id);
dev_dbg(rproc->dev.parent, "%s mailbox: %s: %d", __func__, mb->name, mb->vq_id);
/* not a bad thing if there is no messages, probably
* means that the previous ring kick processed the message
*/
if (rproc_vq_interrupt(rproc, mb->vq_id) == IRQ_NONE)
dev_dbg(&rproc->dev, "no message found in vq%d\n", mb->vq_id);
}
@ -274,12 +298,11 @@ static void bflb_rproc_rx_mbox_callback(struct mbox_client *client, void *data)
struct rproc *rproc = dev_get_drvdata(dev);
struct bflb_rproc *drproc = (struct bflb_rproc *)rproc->priv;
struct bflb_mbox *mb = &drproc->mbox_rx;
struct bflb_mbox_msg *msg = data;
mb->vq_id = (int)data;
dev_info(dev, "%s mailbox %s: %d", __func__, mb->name, mb->vq_id);
mb->vq_id = msg->param;
dev_dbg(dev, "%s mailbox %s: %d", __func__, mb->name, mb->vq_id);
queue_work(drproc->workqueue, &mb->vq_work);
mbox_chan_txdone(mb->chan, 0);
@ -318,7 +341,8 @@ static const struct rproc_ops bflb_rproc_ops = {
.get_loaded_rsc_table = bflb_rproc_get_loaded_rsc_table,
};
static int bflb_rproc_setup_mbox(struct rproc *rproc) {
static int bflb_rproc_setup_mbox(struct rproc *rproc)
{
struct bflb_rproc *drproc = (struct bflb_rproc *)rproc->priv;
struct bflb_mbox *tx_bflb_mbox = &drproc->mbox_tx;
@ -335,7 +359,7 @@ static int bflb_rproc_setup_mbox(struct rproc *rproc) {
/* request the TX mailboxs */
tx_mbox_cl->dev = dev->parent;
tx_mbox_cl->tx_block = true;
tx_mbox_cl->tx_tout = 100;
tx_mbox_cl->tx_tout = 200;
strncpy(tx_bflb_mbox->name, "virtio-tx", sizeof(tx_bflb_mbox->name));
tx_bflb_mbox->chan = mbox_request_channel_byname(tx_mbox_cl, "virtio-tx");
if (IS_ERR(tx_bflb_mbox->chan)) {
@ -397,6 +421,8 @@ static int bflb_rproc_probe(struct platform_device *pdev)
drproc = rproc->priv;
drproc->rproc = rproc;
rproc->has_iommu = false;
rproc->sysfs_read_only = true;
platform_set_drvdata(pdev, rproc);
drproc->workqueue = create_workqueue(dev_name(dev));
@ -456,7 +482,7 @@ static int bflb_rproc_remove(struct platform_device *pdev)
}
static const struct of_device_id davinci_rproc_of_match[] __maybe_unused = {
{ .compatible = "bflb,bl808-rproc", },
{ .compatible = "bflb,bflb-rproc", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, davinci_rproc_of_match);
@ -465,7 +491,7 @@ static struct platform_driver bflb_rproc_driver = {
.probe = bflb_rproc_probe,
.remove = bflb_rproc_remove,
.driver = {
.name = "bl808-rproc",
.name = "bflb-rproc",
.of_match_table = of_match_ptr(davinci_rproc_of_match),
},
};

View file

@ -92,7 +92,8 @@ static int rpmsg_tty_write(struct tty_struct *tty, const u8 *buf, int len)
* Use rpmsg_trysend instead of rpmsg_send to send the message so the caller is not
* hung until a rpmsg buffer is available. In such case rpmsg_trysend returns -ENOMEM.
*/
ret = rpmsg_trysend(rpdev->ept, (void *)buf, msg_size);
/* for bl808, we only have a few small buffers, and a slow M0 CPU, so lets use rpmsg_send */
ret = rpmsg_send(rpdev->ept, (void *)buf, msg_size);
if (ret) {
dev_dbg_ratelimited(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
return ret;

View file

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/*
* BL808 mailbox message format
*
* Copyright (C) 2021 The Asahi Linux Contributors
*/
#ifndef _LINUX_BFLB_MAILBOX_H_
#define _LINUX_BFLB_MAILBOX_H_
#include <linux/types.h>
/* encodes a single 32bit message sent over the single channel */
struct bflb_mbox_msg {
u32 param;
u32 id;
};
#endif