From a70bfde653b44763e2f4d230d30aa8382c1ac5f9 Mon Sep 17 00:00:00 2001 From: Kristofer Jonsson Date: Thu, 12 Jan 2023 10:00:03 +0100 Subject: Add rpmsg capabilities to remoteproc Tie in mbox and reset driver and add support for virtio carveouts. Also move the reserved mem for device mem to the remoteproc node. Change-Id: I7e8878d32ae7c02d5b43198e45652e77f8ff79ca --- remoteproc/ethosu_remoteproc.c | 435 +++++++++++++++++++++++------------------ 1 file changed, 246 insertions(+), 189 deletions(-) (limited to 'remoteproc') diff --git a/remoteproc/ethosu_remoteproc.c b/remoteproc/ethosu_remoteproc.c index 3b45071..b1f8793 100644 --- a/remoteproc/ethosu_remoteproc.c +++ b/remoteproc/ethosu_remoteproc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 Arm Limited. All rights reserved. + * Copyright 2021-2023 Arm Limited and/or its affiliates * * This program is free software and is provided to you under the terms of the * GNU General Public License version 2 as published by the Free Software @@ -17,26 +17,37 @@ * * SPDX-License-Identifier: GPL-2.0-only */ - +#include #include #include +#include #include +#include #include #include #include #include +#include #include #include #include #include +#include + +/**************************************************************************** + * Defines + ****************************************************************************/ + +#define DMA_ADDR_BITS 32 /* Number of address bits */ #define ETHOSU_RPROC_DRIVER_VERSION "0.0.1" #define DEFAULT_FW_FILE "arm-ethos-u65.fw" -#define DEFAULT_AUTO_BOOT (false) +#define DEFAULT_AUTO_BOOT false /* firmware naming module parameter */ static char fw_filename_param[256] = DEFAULT_FW_FILE; + /* As the remoteproc is setup at probe, just allow the filename readonly */ module_param_string(filename, fw_filename_param, sizeof(fw_filename_param), 0444); @@ -44,218 +55,241 @@ MODULE_PARM_DESC(filename, "Filename for firmware image for Ethos-U remoteproc"); static bool auto_boot = DEFAULT_AUTO_BOOT; -module_param(auto_boot, bool, DEFAULT_AUTO_BOOT); +module_param(auto_boot, bool, 0); MODULE_PARM_DESC(auto_boot, "Set to one to auto boot at load."); struct ethosu_rproc { - struct device *dev; - struct reset_control *rstc; - struct rproc_mem_mapping *map; - size_t map_size; + struct device *dev; + struct reset_control *rstc; + struct mbox_client mbox_client; + struct mbox_chan *ch_rx; + struct mbox_chan *ch_tx; + struct workqueue_struct *wq; + struct work_struct work; }; -struct rproc_mem_mapping { - const char *name; - phys_addr_t rproc_addr; - void __iomem *vaddr; - size_t size; -}; - -struct ethosu_rproc_config { - struct fw_config *fw; -}; +/* declaration is in remoteproc_internal.h */ +extern irqreturn_t rproc_vq_interrupt(struct rproc *rproc, + int vq_id); -/*****************************************************************************/ - -static int ethosu_rproc_start(struct rproc *rproc) +static void ethosu_mbox_bottom(struct work_struct *work) { - struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv; - struct device *dev = ethosu->dev; + struct ethosu_rproc *erproc = container_of( + work, struct ethosu_rproc, work); + struct rproc *rproc = dev_get_drvdata(erproc->dev); - dev_info(dev, "Starting up Ethos-U subsystem CPU!"); + dev_dbg(&rproc->dev, "Handle interrupt"); - return reset_control_deassert(ethosu->rstc); + rproc_vq_interrupt(rproc, 0); } -static int ethosu_rproc_stop(struct rproc *rproc) +static void ethosu_mbox_top(struct mbox_client *client, + void *message) { - struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv; - struct device *dev = ethosu->dev; + struct ethosu_rproc *erproc = container_of( + client, struct ethosu_rproc, mbox_client); - dev_info(dev, "Stopping Ethos-U subsystem CPU!"); - - return reset_control_assert(ethosu->rstc); + queue_work(erproc->wq, &erproc->work); } -static void ethosu_rproc_kick(struct rproc *rproc, - int vqid) +static dma_addr_t ethosu_of_pa_to_da(struct rproc *rproc, + const phys_addr_t pa, + const size_t size) { - return; -} + static const char of_rproc_ranges[] = "ethosu,dma-ranges"; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) -static void *ethosu_da_to_va(struct rproc *rproc, - u64 da, - size_t len, - bool *is_iomem) -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) -static void *ethosu_da_to_va(struct rproc *rproc, - u64 da, - size_t len) -#else -static void *ethosu_da_to_va(struct rproc *rproc, - u64 da, - int len) -#endif -{ - struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv; - int offset; + struct device *dev = rproc->dev.parent; + struct device_node *np = dev->of_node; + const __be32 *rproc_ranges = of_get_property(np, of_rproc_ranges, NULL); + const int addr_cells = of_n_addr_cells(np); + const int size_cells = of_n_size_cells(np); + const int ranges_cells = addr_cells + addr_cells + size_cells; + int ranges_cnt; int i; - for (i = 0; i < ethosu->map_size; i++) - if (da >= ethosu->map[i].rproc_addr && - da < (ethosu->map[i].rproc_addr + ethosu->map[i].size)) { - offset = da - ethosu->map[i].rproc_addr; - dev_info(ethosu->dev, - "mapping %llx to %p (offset: 0x%x)", da, - (void *)(ethosu->map[i].vaddr + offset), - offset); + ranges_cnt = of_property_count_elems_of_size( + np, of_rproc_ranges, ranges_cells * sizeof(u32)); - return (void *)(ethosu->map[i].vaddr + offset); - } + for (i = 0; i < ranges_cnt; i++) { + const int offset = i * ranges_cells; - return NULL; -} + const uint64_t of_da = of_read_number( + &rproc_ranges[offset], addr_cells); + const uint64_t of_pa = of_read_number( + &rproc_ranges[offset + addr_cells], addr_cells); + const uint64_t of_size = of_read_number( + &rproc_ranges[offset + addr_cells + addr_cells], + size_cells); -static const struct rproc_ops ethosu_rproc_ops = { - .start = ðosu_rproc_start, - .stop = ðosu_rproc_stop, - .kick = ðosu_rproc_kick, - .da_to_va = ðosu_da_to_va, -}; + if (pa >= of_pa && (pa + size) <= (of_pa + of_size)) { + const dma_addr_t da = of_da + pa - of_pa; -/** - * Since the remote side doesn't yet support rpmsg just return an - * empty resource table when asked about it. - */ -struct resource_table *ethosu_rproc_find_rsc_table(struct rproc *rproc, - const struct firmware *fw, - int *tablesz) -{ - static struct resource_table table = { .ver = 1, }; - struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv; + dev_dbg(dev, "PA to DA. pa=0x%llx, da=0x%llx", pa, da); - dev_info(ethosu->dev, "Sizeof struct resource_table : %zu", - sizeof(table)); - *tablesz = sizeof(table); + return da; + } + } - return &table; + return (dma_addr_t)(-1); } -/*****************************************************************************/ - -static int ethosu_rproc_of_memory_translations(struct platform_device *pdev, - struct ethosu_rproc *ethosu_rproc) +static int ethosu_add_carveout(struct rproc *rproc, + const phys_addr_t pa, + const size_t size, + const char *name) { - const char *const of_rproc_address_cells = - "#ethosu,rproc-address-cells"; - const char *const of_rproc_ranges = "ethosu,rproc-ranges"; - const char *const of_rproc_ranges_names = "ethosu,rproc-names"; + struct device *dev = rproc->dev.parent; + dma_addr_t da; + void __iomem *va; + struct rproc_mem_entry *mem; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct rproc_mem_mapping *mem_map; - const __be32 *rproc_ranges; + da = ethosu_of_pa_to_da(rproc, pa, size); + if (da == (dma_addr_t)(-1)) { + dev_err(dev, "No mapping found for PA. pa=%llx, size=%zu", pa, + size); - int addr_cells, rproc_addr_cells, size_cells, cells_for_array_element; - int i, len, cnt, name_cnt, ret = 0; + return -ENOMEM; + } - if (of_property_read_u32(np, of_rproc_address_cells, - &rproc_addr_cells)) { - dev_info(dev, "%s not defined in dtb", of_rproc_address_cells); + va = devm_ioremap_wc(dev, pa, size); + if (!va) { + dev_err(dev, "Failed to remap address. pa=%llx, len=%zu", pa, + size); - return -ENODEV; + return -ENOMEM; } - addr_cells = of_n_addr_cells(np); - size_cells = of_n_size_cells(np); + mem = rproc_mem_entry_init(dev, va, pa, size, da, NULL, NULL, name); + if (!mem) + return -ENOMEM; - dev_dbg(dev, "Using %d remote proc address cells for parsing mapping", - rproc_addr_cells); - dev_dbg(dev, - "Using %d of size %d parent address cells for parsing mapping", - addr_cells, size_cells); + dev_info(dev, "Add carveout mapping. dma=%llx, da=%x, va=%p, len=%zu", + mem->dma, mem->da, mem->va, mem->len); - cells_for_array_element = addr_cells + rproc_addr_cells + size_cells; + rproc_add_carveout(rproc, mem); - cnt = of_property_count_elems_of_size(np, of_rproc_ranges, - cells_for_array_element); - cnt /= sizeof(u32); + return 0; +} - if (cnt <= 0) { - dev_info(dev, "No remoteproc memory mapping ranges found."); +static int ethosu_rproc_prepare(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + struct device_node *np = dev->of_node; + struct of_phandle_iterator it; + struct resource res; + int i; + int ret; - return 0; - } + dev_info(dev, "Preparing Ethos-U"); - name_cnt = of_property_count_strings(np, of_rproc_ranges_names); - if (name_cnt > 0 && name_cnt != cnt) { - dev_err(dev, "Mismatch length for %s and %s", of_rproc_ranges, - of_rproc_ranges_names); + /* Add carveout for each 'reg' device tree entry */ + for (i = 0; of_address_to_resource(np, i, &res) == 0; i++) { + dev_info(dev, "Found resource. start=%llx, size=%llx", + res.start, resource_size(&res)); - return -EINVAL; + ret = ethosu_add_carveout(rproc, res.start, + resource_size(&res), res.name); + if (ret) + return ret; } - mem_map = devm_kcalloc(dev, cnt, sizeof(*mem_map), GFP_KERNEL); - if (!mem_map) - return -ENOMEM; + of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); + while (of_phandle_iterator_next(&it) == 0) { + struct reserved_mem *res_mem = of_reserved_mem_lookup(it.node); - rproc_ranges = of_get_property(np, of_rproc_ranges, &len); - - for (i = 0; i < cnt; i++) { - struct resource *r; - const char *name = NULL; - int n; - - of_property_read_string_index(np, of_rproc_ranges_names, i, - &name); - mem_map[i].name = name; - n = i * cells_for_array_element; - mem_map[i].rproc_addr = - of_read_number(&rproc_ranges[n + addr_cells], - rproc_addr_cells); - mem_map[i].size = - of_read_number(&rproc_ranges[n + addr_cells + - rproc_addr_cells], - size_cells); - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); - if (!r) { - dev_err(&pdev->dev, "Failed to get '%s' resource.\n", - name); + if (!res_mem) { + dev_err(dev, "Failed to look up memory region. node=%p", + it.node); return -EINVAL; } - mem_map[i].vaddr = devm_ioremap_wc(dev, r->start, - mem_map[i].size); - if (IS_ERR(mem_map[i].vaddr)) { - dev_err(dev, "Failed to remap '%s'", name); + dev_info(dev, + "Found memory region. pa=%llx, size=%llu, name=%s", + res_mem->base, res_mem->size, it.node->name); - return PTR_ERR(mem_map[i].vaddr); - } + ret = ethosu_add_carveout(rproc, res_mem->base, res_mem->size, + it.node->name); + if (ret) + return ret; + } + + return 0; +} + +static int ethosu_rproc_start(struct rproc *rproc) +{ + struct ethosu_rproc *erproc = (struct ethosu_rproc *)rproc->priv; + struct device *dev = erproc->dev; + + dev_info(dev, "Starting up Ethos-U subsystem CPU"); - dev_dbg(dev, - "rproc memory mapping[%i]=%s: da %llx, va, %pa, size %zx:\n", - i, name, mem_map[i].rproc_addr, &mem_map[i].vaddr, - mem_map[i].size); + return reset_control_deassert(erproc->rstc); +} + +static int ethosu_rproc_stop(struct rproc *rproc) +{ + struct ethosu_rproc *erproc = (struct ethosu_rproc *)rproc->priv; + struct device *dev = erproc->dev; + + dev_info(dev, "Stopping Ethos-U subsystem CPU"); + + return reset_control_assert(erproc->rstc); +} + +static void ethosu_rproc_kick(struct rproc *rproc, + int vqid) +{ + struct ethosu_rproc *erproc = (struct ethosu_rproc *)rproc->priv; + + dev_dbg(&rproc->dev, "Kicking Ethos-U remoteproc vqid: %d!", vqid); + + mbox_send_message(erproc->ch_tx, (void *)&vqid); +} + +static const struct rproc_ops ethosu_rproc_ops = { + .prepare = ðosu_rproc_prepare, + .start = ðosu_rproc_start, + .stop = ðosu_rproc_stop, + .kick = ðosu_rproc_kick, +}; + +static int ethosu_mailbox_init(struct ethosu_rproc *erproc) +{ + struct device *dev = erproc->dev; + struct mbox_client *cl = &erproc->mbox_client; + + INIT_WORK(&erproc->work, ethosu_mbox_bottom); + + erproc->wq = create_singlethread_workqueue("ethosu_rproc_wq"); + if (!erproc->wq) { + dev_err(dev, "Failed to create work queue"); + + return -EINVAL; } - ethosu_rproc->map = mem_map; - ethosu_rproc->map_size = cnt; - dev_dbg(dev, "rproc memory mapped %zx regions", ethosu_rproc->map_size); + cl->dev = dev; + cl->rx_callback = ethosu_mbox_top; + cl->tx_prepare = NULL; + cl->tx_done = NULL; + cl->tx_block = true; + cl->knows_txdone = false; + cl->tx_tout = 500; - return ret; + erproc->ch_rx = mbox_request_channel_byname(cl, "rx"); + if (IS_ERR(erproc->ch_rx)) { + dev_err(dev, "Failed to request mbox chan rx"); + + return PTR_ERR(erproc->ch_rx); + } + + erproc->ch_tx = mbox_request_channel_byname(cl, "tx"); + if (IS_ERR(erproc->ch_tx)) { + dev_info(dev, "Using same channel for RX and TX"); + erproc->ch_tx = erproc->ch_rx; + } + + return 0; } static const struct of_device_id ethosu_rproc_match[] = { @@ -267,47 +301,62 @@ static int ethosu_rproc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct ethosu_rproc *ethosu_rproc; + struct ethosu_rproc *erproc; struct rproc *rproc; - int ret = -ENODEV; - - rproc = rproc_alloc(dev, np->name, ðosu_rproc_ops, - fw_filename_param, - sizeof(*ethosu_rproc)); - if (!rproc) { - ret = -ENOMEM; - goto out; - } + int ret; + + /* map the first 'memory-region' for DMA-mapping */ + ret = of_reserved_mem_device_init(dev); + if (ret) + return ret; + + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(DMA_ADDR_BITS)); + + rproc = devm_rproc_alloc(dev, np->name, ðosu_rproc_ops, + fw_filename_param, + sizeof(*erproc)); + if (!rproc) + return -ENOMEM; + + platform_set_drvdata(pdev, rproc); /* Configure rproc */ rproc->has_iommu = false; rproc->auto_boot = auto_boot; - platform_set_drvdata(pdev, rproc); - - ethosu_rproc = rproc->priv; - ethosu_rproc->dev = dev; + /* Configure Ethos-U rproc */ + erproc = rproc->priv; + erproc->dev = dev; /* Get the reset handler for the subsystem */ - ethosu_rproc->rstc = devm_reset_control_get_exclusive_by_index(dev, 0); - if (IS_ERR(ethosu_rproc->rstc)) { - dev_err(&pdev->dev, "Failed to get reset controller.\n"); - ret = PTR_ERR(ethosu_rproc->rstc); - goto free_rproc; + erproc->rstc = devm_reset_control_get_exclusive_by_index(dev, 0); + if (IS_ERR(erproc->rstc)) { + dev_err(&pdev->dev, "Failed to get reset controller."); + + return PTR_ERR(erproc->rstc); } - /* Get the translation from device memory to kernel space */ - ret = ethosu_rproc_of_memory_translations(pdev, ethosu_rproc); + /* Allocate and initialize mailbox client */ + ret = ethosu_mailbox_init(erproc); if (ret) - goto free_rproc; + return ret; ret = rproc_add(rproc); + if (ret) { + dev_err(dev, "Failed to add rproc"); + goto free_mbox; + } -free_rproc: - if (ret) - rproc_free(rproc); + return 0; + +free_mbox: + if (erproc->wq) + destroy_workqueue(erproc->wq); + + mbox_free_channel(erproc->ch_rx); -out: + if (erproc->ch_tx != erproc->ch_rx) + mbox_free_channel(erproc->ch_tx); return ret; } @@ -315,9 +364,17 @@ out: static int ethosu_rproc_remove(struct platform_device *pdev) { struct rproc *rproc = platform_get_drvdata(pdev); + struct ethosu_rproc *erproc = rproc->priv; + + if (erproc->wq) + destroy_workqueue(erproc->wq); + + if (erproc->ch_tx != erproc->ch_rx) + mbox_free_channel(erproc->ch_tx); + + mbox_free_channel(erproc->ch_rx); rproc_del(rproc); - rproc_free(rproc); return 0; } -- cgit v1.2.1