From 9348e10221d2b9c3d8a9b0d4dee5591b190126e8 Mon Sep 17 00:00:00 2001 From: Justin Hammond Date: Sat, 4 Feb 2023 00:16:15 +0800 Subject: [PATCH] WIP: Remoteproc support for bl808 --- drivers/remoteproc/Kconfig | 9 + drivers/remoteproc/Makefile | 2 + drivers/remoteproc/bl808_remoteproc.c | 389 ++++++++++++++++++++++++++ 3 files changed, 400 insertions(+) create mode 100644 drivers/remoteproc/bl808_remoteproc.c diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index a850e9f486dd..0cfb47fbd2e5 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -161,6 +161,15 @@ config PRU_REMOTEPROC processors on various TI SoCs. It's safe to say N here if you're not interested in the PRU or if you are unsure. +config BL808_REMOTEPROC + tristate "BL808 remoteproc support" + select MAILBOX + help + Support for Bouffalo Labs BL808 remote processors M0/LP + subsystem via the remote processor framework. + + Say Y or M here to enable + config QCOM_PIL_INFO tristate diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 91314a9b43ce..44de5b26ed0e 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -39,3 +39,5 @@ obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o +obj-$(CONFIG_BL808_REMOTEPROC) += bl808_remoteproc.o + diff --git a/drivers/remoteproc/bl808_remoteproc.c b/drivers/remoteproc/bl808_remoteproc.c new file mode 100644 index 000000000000..10a3db04f104 --- /dev/null +++ b/drivers/remoteproc/bl808_remoteproc.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Remote processor machine-specific module for bl808 + * + * Copyright (C) 2023 Justin Hammond + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "remoteproc_internal.h" + + +/** + * struct bl808_rproc - bl808 remote processor instance state + * @rproc: rproc handle + * @mbox: the mailbox channel + * @client: the mailbox client + */ +struct bl808_rproc { + struct rproc *rproc; + struct mbox_chan *mbox; + struct mbox_client client; +}; + +/* The feature bitmap for virtio rpmsg */ +#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ + +#define FW_RSC_U32_ADDR_ANY 0xFFFFFFFFUL + +#define RPMSG_VDEV_DFEATURES (1 << VIRTIO_RPMSG_F_NS) + +/* VirtIO rpmsg device id */ +#define VIRTIO_ID_RPMSG_ 7 + +/* Resource table entries */ +#define NUM_VRINGS 0x02 +#define VRING_ALIGN 0x1000 +#define RING_TX FW_RSC_U32_ADDR_ANY +#define RING_RX FW_RSC_U32_ADDR_ANY +#define VRING_SIZE 2 + +#define NUM_TABLE_ENTRIES 1 + +#define NO_RESOURCE_ENTRIES 1 + +/* this is normally in the header of the ELF files for firmware + * but since M0 is already running and there isn't a ELF file on + * flash for it, we fudge the resource table header to statically + * specify our virtio rings etc + */ +struct remote_resource_table { + u32 version; + u32 num; + u32 reserved[2]; + u32 offset[NO_RESOURCE_ENTRIES]; + /* rpmsg vdev entry */ + u32 type; + struct fw_rsc_vdev rpmsg_vdev; + struct fw_rsc_vdev_vring rpmsg_vring0; + struct fw_rsc_vdev_vring rpmsg_vring1; +}__packed; + +/* this is our fudged ELF Resource Table Header setup with + * one rpmsg virtio device and two virtio rings + */ +struct remote_resource_table resources = { + /* Version */ + .version = 1, + + /* NUmber of table entries */ + .num = NUM_TABLE_ENTRIES, + + /* reserved fields */ + .reserved = {0, 0,}, + + /* Offsets of rsc entries */ + .offset[0] = offsetof(struct remote_resource_table, type), + + .type = RSC_VDEV, + /* Virtio device entry */ + { + VIRTIO_ID_RPMSG_, 0, RPMSG_VDEV_DFEATURES, 0, 0, 0, + NUM_VRINGS, {0, 0}, + }, + + /* Vring rsc entry - part of vdev rsc entry */ + {RING_TX, VRING_ALIGN, VRING_SIZE, 1, 0}, + {RING_RX, VRING_ALIGN, VRING_SIZE, 2, 0}, +}; + +/* return a pointer to our resource table + */ +struct resource_table *bl808_rproc_get_loaded_rsc_table(struct rproc *rproc, size_t *size) +{ + struct device *dev = rproc->dev.parent; + dev_dbg(dev, "bl808_rproc_get_loaded_rsc_table"); + + *size = sizeof(resources); + return (struct resource_table *)&resources; +} + +/* allocate vdev0buffer */ +static int bl808_rproc_mem_alloc(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + struct device *dev = rproc->dev.parent; + void *va; + + dev_dbg(dev, "Allocating memory region"); + + va = ioremap_wc(mem->dma, mem->len); + if (!va) { + dev_err(dev, "Unable to map memory region: %pa+%zx\n", + &mem->dma, mem->len); + return -ENOMEM; + } + + /* Update memory entry va */ + mem->va = va; + dev_dbg(dev, "Allocated memory region: %pa+%zx -> %p", &mem->dma, mem->len, mem->va); + + return 0; +} + +/* release vdev0buffer */ +static int bl808_rproc_mem_release(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + struct device *dev = rproc->dev.parent; + + dev_dbg(dev, "release memory region"); + + iounmap(mem->va); + + return 0; +} + +/* + * Pull the memory ranges for virtio from the device tree and register them + */ +static int bl808_rproc_setupmem(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + struct device_node *np = dev->of_node; + struct rproc_mem_entry *mem; + struct reserved_mem *rmem; + struct of_phandle_iterator it; + int index = 0; + + dev_dbg(dev, "bl808_rproc_parse_fw %s", np->name); + + of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); + while (of_phandle_iterator_next(&it) == 0) { + dev_dbg(dev, "memory-region %s", it.node->name); + rmem = of_reserved_mem_lookup(it.node); + if (!rmem) { + dev_err(dev, "unable to acquire memory-region\n"); + return -EINVAL; + } + dev_dbg(dev, "memory-region %s", it.node->name); + + /* No need to map vdev buffer */ + if (strcmp(it.node->name, "vdev0buffer")) { + /* Register memory region */ + dev_dbg(dev, "registering memory region %s", it.node->name); + mem = rproc_mem_entry_init(dev, NULL, + (dma_addr_t)rmem->base, + rmem->size, rmem->base, + bl808_rproc_mem_alloc, + bl808_rproc_mem_release, + it.node->name); + } else { + /* Register reserved memory for vdev buffer allocation */ + dev_dbg(dev, "registering reserved memory region %s", it.node->name); + mem = rproc_of_resm_mem_entry_init(dev, index, + rmem->size, + rmem->base, + it.node->name); + } + + if (!mem) { + dev_err(dev, "unable to allocate memory entry %s", it.node->name); + return -ENOMEM; + } + rproc_add_carveout(rproc, mem); + index++; + } + return 0; +} + + +/* M0 is already started. Do Nothing + */ +static int bl808_rproc_start(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + + dev_dbg(dev, "bl808_rproc_start"); + + return 0; +} + +/* We don't want to stop M0, as it will crash. Do Nothing +*/ +static int bl808_rproc_stop(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + + dev_dbg(dev, "bl808_rproc_stop"); + + return 0; +} + +/* kick the virtqueue to let M0 know there is a update to the vring + */ +static void bl808_rproc_kick(struct rproc *rproc, int vqid) +{ + struct device *dev = rproc->dev.parent; + struct bl808_rproc *drproc = (struct bl808_rproc *)rproc->priv; + + /* Kick the other CPU to let it know the vrings are updated */ + dev_dbg(dev, "bl808_rproc_kick %d", vqid); + mbox_send_message(drproc->mbox, (void*)vqid); +} + +/* M0 signaled us there is a update on the vring, check it + */ +static void bflb_rproc_mbox_callback(struct mbox_client *client, void *data) +{ + struct device *dev = client->dev; + struct rproc *rproc = dev_get_drvdata(dev); + u32 vqid = (u32)data; + + dev_dbg(dev, "bflb_rproc_mbox_callback %d", vqid); + + if (vqid > 0 && vqid < 3) + rproc_vq_interrupt(rproc, vqid); + else + dev_err(dev, "bflb_rproc_mbox_callback: Invalid vqid %d", vqid); +} + +/* M0 is already running when we boot + * so just attach to it. + * we also register a mailbox to get kicks from M0 when vrings are updated + */ +static int bl808_rproc_attach(struct rproc *rproc) +{ + struct device *dev = &rproc->dev; + struct bl808_rproc *drproc = (struct bl808_rproc *)rproc->priv; + int ret; + + /* request the mailbox */ + struct mbox_client *vq1_mbox = &drproc->client; + vq1_mbox->dev = dev->parent; + dev_dbg(vq1_mbox->dev, "bl808_rproc_attachasdfadfsdf: Attaching to %s", rproc->name); + vq1_mbox->tx_done = NULL; + vq1_mbox->rx_callback = bflb_rproc_mbox_callback; + vq1_mbox->tx_block = false; + vq1_mbox->knows_txdone = false; + + drproc->mbox = mbox_request_channel(vq1_mbox, 0); + if (IS_ERR(drproc->mbox)) { + ret = -EBUSY; + dev_err(dev, "mbox_request_channel failed: %ld\n", + PTR_ERR(drproc->mbox)); + return ret; + } + + dev_dbg(dev, "bl808_rproc_attach: Attaching to %s", rproc->name); + return 0; +} + +/* Detach. Do Nothing? */ + +static int bl808_rproc_detach(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + + dev_dbg(dev, "bl808_rproc_detach: Detaching from %s", rproc->name); + return 0; +} + +static const struct rproc_ops bl808_rproc_ops = { + .start = bl808_rproc_start, + .stop = bl808_rproc_stop, + .attach = bl808_rproc_attach, + .detach = bl808_rproc_detach, + .kick = bl808_rproc_kick, + .prepare = bl808_rproc_setupmem, + .get_loaded_rsc_table = bl808_rproc_get_loaded_rsc_table, +}; + + +static int bl808_rproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bl808_rproc *drproc; + struct rproc *rproc; + int ret; + + dev_dbg(dev, "bl808_rproc_probe "); + + rproc = rproc_alloc(dev, "M0", &bl808_rproc_ops, NULL, + sizeof(*drproc)); + if (!rproc) { + ret = -ENOMEM; + goto free_mem; + } + + /* error recovery is not supported at present */ + rproc->recovery_disabled = true; + + /* M0 is running when linux boots */ + //atomic_inc(&rproc->power); + rproc->state = RPROC_DETACHED; + + drproc = rproc->priv; + drproc->rproc = rproc; + rproc->has_iommu = false; + + ret = rproc_add(rproc); + if (ret) { + dev_err(dev, "rproc_add failed: %d\n", ret); + goto free_rproc; + } + + platform_set_drvdata(pdev, rproc); + + dev_dbg(dev, "rproc_add success"); + + return 0; + +free_rproc: + rproc_free(rproc); +free_mem: + if (dev->of_node) + of_reserved_mem_device_release(dev); + return ret; +} + +static int bl808_rproc_remove(struct platform_device *pdev) +{ + struct rproc *rproc = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + dev_dbg(dev, "bl808_rproc_remove"); + + /*XXX TODO: we need to unregister our mailbox? */ + + rproc_del(rproc); + rproc_free(rproc); + if (dev->of_node) + of_reserved_mem_device_release(dev); + + return 0; +} + +static const struct of_device_id davinci_rproc_of_match[] __maybe_unused = { + { .compatible = "bflb,bl808-rproc", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, davinci_rproc_of_match); + +static struct platform_driver bl808_rproc_driver = { + .probe = bl808_rproc_probe, + .remove = bl808_rproc_remove, + .driver = { + .name = "bl808-rproc", + .of_match_table = of_match_ptr(davinci_rproc_of_match), + }, +}; + +module_platform_driver(bl808_rproc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("justin@dynam.ac"); +MODULE_DESCRIPTION("bl808 Remote Processor control driver");