mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:59:29 +02:00
remoteproc: Add AMD MicroBlaze/V BRAM-based remote processor driver
Add a remoteproc driver for AMD MicroBlaze/V soft-core processor subsystems instantiated in programmable logic and using dual-port BRAM for firmware storage and execution. The driver parses the firmware memory window from the remoteproc device node's reg property, interprets that address and size in the processor-local address space, and then uses standard devicetree address translation through the parent bus ranges property to obtain the corresponding Linux-visible system physical address. The resulting translated region is registered as the executable remoteproc carveout and coredump segment. The processor is controlled through an active-low reset GPIO and a subsystem clock. The clock is enabled before reset is released, and the processor is kept in reset until firmware loading completes. The firmware-name property is optional, allowing firmware to be assigned later through the remoteproc framework. Firmware images without a resource table are also accepted. Signed-off-by: Ben Levinsky <ben.levinsky@amd.com> Link: https://lore.kernel.org/r/20260714202441.554065-3-ben.levinsky@amd.com Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
This commit is contained in:
committed by
Mathieu Poirier
parent
8527ebd21a
commit
f87b499899
@@ -23,6 +23,15 @@ config REMOTEPROC_CDEV
|
||||
|
||||
It's safe to say N if you don't want to use this interface.
|
||||
|
||||
config AMD_MBV_BRAM_REMOTEPROC
|
||||
tristate "AMD MicroBlaze/V BRAM-based remoteproc support"
|
||||
depends on OF && COMMON_CLK && (GPIOLIB || COMPILE_TEST)
|
||||
help
|
||||
Say y or m here to support a MicroBlaze/V BRAM-based remote
|
||||
processor managed through the remoteproc framework.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config IMX_REMOTEPROC
|
||||
tristate "i.MX remoteproc support"
|
||||
depends on ARCH_MXC
|
||||
|
||||
@@ -11,6 +11,7 @@ remoteproc-y += remoteproc_sysfs.o
|
||||
remoteproc-y += remoteproc_virtio.o
|
||||
remoteproc-y += remoteproc_elf_loader.o
|
||||
obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o
|
||||
obj-$(CONFIG_AMD_MBV_BRAM_REMOTEPROC) += amd_mbv_bram_rproc.o
|
||||
obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
|
||||
obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o
|
||||
obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AMD MicroBlaze/V BRAM-based Remote Processor driver
|
||||
*
|
||||
* Copyright (C) 2026 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* This driver supports soft-core processors (MicroBlaze, MicroBlaze-V, or
|
||||
* similar) instantiated in AMD programmable logic, using dual-port BRAM
|
||||
* for firmware storage and execution.
|
||||
*
|
||||
* The firmware memory (BRAM) is described in the processor-local address
|
||||
* space and translated to the Linux-visible system physical address with
|
||||
* standard devicetree address translation.
|
||||
*
|
||||
* Reset is controlled via GPIO connected to Processor System Reset IP.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/remoteproc.h>
|
||||
|
||||
#include "remoteproc_internal.h"
|
||||
|
||||
/**
|
||||
* struct amd_bram_rproc - AMD MicroBlaze/V BRAM-based remoteproc private data
|
||||
* @dev: device pointer
|
||||
* @reset: GPIO descriptor for reset control (active-low)
|
||||
* @clk: processor clock
|
||||
*/
|
||||
struct amd_bram_rproc {
|
||||
struct device *dev;
|
||||
struct gpio_desc *reset;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static int amd_bram_rproc_prepare(struct rproc *rproc)
|
||||
{
|
||||
struct amd_bram_rproc *priv = rproc->priv;
|
||||
struct rproc_mem_entry *mem;
|
||||
struct resource res;
|
||||
u64 da, size;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_reg(priv->dev->of_node, 0, &da, &size);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to parse executable memory reg\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!size || size > U32_MAX) {
|
||||
dev_err(priv->dev, "invalid executable memory size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (da > U32_MAX) {
|
||||
dev_err(priv->dev, "invalid executable memory address\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(priv->dev->of_node, 0, &res);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to translate executable memory reg\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)res.start,
|
||||
resource_size(&res), da,
|
||||
rproc_mem_entry_ioremap_wc,
|
||||
rproc_mem_entry_iounmap,
|
||||
dev_name(priv->dev));
|
||||
if (!mem)
|
||||
return -ENOMEM;
|
||||
|
||||
rproc_add_carveout(rproc, mem);
|
||||
rproc_coredump_add_segment(rproc, da, resource_size(&res));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_bram_rproc_start(struct rproc *rproc)
|
||||
{
|
||||
struct amd_bram_rproc *priv = rproc->priv;
|
||||
int ret;
|
||||
|
||||
/* Enable clock before releasing reset */
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to enable clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Deassert reset and let the processor run. */
|
||||
ret = gpiod_set_value_cansleep(priv->reset, 0);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to deassert reset: %d\n", ret);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_bram_rproc_stop(struct rproc *rproc)
|
||||
{
|
||||
struct amd_bram_rproc *priv = rproc->priv;
|
||||
int ret;
|
||||
|
||||
/* Assert reset before disabling the processor clock. */
|
||||
ret = gpiod_set_value_cansleep(priv->reset, 1);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to assert reset: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Disable clock after asserting reset */
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_bram_rproc_parse_fw(struct rproc *rproc,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
rproc_elf_load_rsc_table_optional(rproc, fw, dev_dbg,
|
||||
"no resource table found\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rproc_ops amd_bram_rproc_ops = {
|
||||
.prepare = amd_bram_rproc_prepare,
|
||||
.start = amd_bram_rproc_start,
|
||||
.stop = amd_bram_rproc_stop,
|
||||
.load = rproc_elf_load_segments,
|
||||
.sanity_check = rproc_elf_sanity_check,
|
||||
.get_boot_addr = rproc_elf_get_boot_addr,
|
||||
.parse_fw = amd_bram_rproc_parse_fw,
|
||||
};
|
||||
|
||||
static int amd_bram_rproc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct amd_bram_rproc *priv;
|
||||
const char *fw_name = NULL;
|
||||
struct rproc *rproc;
|
||||
int ret;
|
||||
|
||||
ret = rproc_of_parse_firmware(dev, 0, &fw_name);
|
||||
if (ret < 0 && ret != -EINVAL)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to parse firmware-name property\n");
|
||||
|
||||
rproc = devm_rproc_alloc(dev, dev_name(dev), &amd_bram_rproc_ops,
|
||||
fw_name, sizeof(*priv));
|
||||
if (!rproc)
|
||||
return -ENOMEM;
|
||||
|
||||
priv = rproc->priv;
|
||||
priv->dev = dev;
|
||||
|
||||
/* Get the processor clock */
|
||||
priv->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(priv->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->clk),
|
||||
"failed to get clock\n");
|
||||
|
||||
/*
|
||||
* Keep the processor in reset until remoteproc has finished loading
|
||||
* firmware into the executable memory window described by reg and
|
||||
* translated through the parent bus ranges property.
|
||||
*/
|
||||
priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(priv->reset))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->reset),
|
||||
"failed to get reset gpio\n");
|
||||
|
||||
rproc->auto_boot = false;
|
||||
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to set DMA mask\n");
|
||||
|
||||
platform_set_drvdata(pdev, rproc);
|
||||
|
||||
ret = devm_rproc_add(dev, rproc);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to register rproc\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id amd_bram_rproc_of_match[] = {
|
||||
{ .compatible = "xlnx,zynqmp-bram-rproc" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, amd_bram_rproc_of_match);
|
||||
|
||||
static struct platform_driver amd_bram_rproc_driver = {
|
||||
.probe = amd_bram_rproc_probe,
|
||||
.driver = {
|
||||
.name = "amd-bram-rproc",
|
||||
.of_match_table = amd_bram_rproc_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(amd_bram_rproc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("AMD MicroBlaze/V BRAM-based Remote Processor driver");
|
||||
MODULE_AUTHOR("Ben Levinsky <ben.levinsky@amd.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user