![Linus Torvalds](/assets/img/avatar_default.png)
This adds support for the STM32 remoteproc, additional i.MX platforms with Cortex M4 remoteprocs and Qualcomm's QCS404 Compute DSP. Initial support for vendor specific resource table entries and support for unprocessed Qualcomm firmware files. -----BEGIN PGP SIGNATURE----- iQJPBAABCAA5FiEEBd4DzF816k8JZtUlCx85Pw2ZrcUFAl0uGwsbHGJqb3JuLmFu ZGVyc3NvbkBsaW5hcm8ub3JnAAoJEAsfOT8Nma3FVhcQALBcuPOo2nBQEiOvgS79 0kVBSdUxw6+mI4EFLfQPeSTtGGrSK5/oZBmYhdv+HEDu6CLG1DS+6PZyCjhqdDsU xjeTIkfe4RDAwnqlGxqK7OyeslAUe91kr0FDaS0XyajhQ+urI8N9Ou/JQDEZKArl lpuxGmjWASeIbO3TC/p4OhyOU6yqqlyxlv9x/kJnF+PhDfdK1tezvgmLvCDtnD9y gtYMzSz9txQCXMJmUaDFeUrMph6/Eo2Zn9rYyen5fDzqAXBys7nfH2fbDcbNK1fy qgjWDjUxENeoZEuohIFn2Dg3egr6rF9TQQvM4uoAoKlS6+yEvTiE8g122BJxVGd3 BOmWK44hTpV1qAGfTivGOH3g/Q9pxI22a17t1BTxcHIYcmMBxERHAVRQ/wbwXRDp qEv4HUuvciMSjonf8pW3GuuOny9jeTird5q8e3gTNA/s6aESm/YoynwWF3LmNZBo XePc5sP4oz9HGJKS/SUu0/H6d1/YqOXOwVBmlTl4L0ErWOpaeGMinJQvSRHb0rL2 BgL8bADxFJGM0ITeIA5CpzZzbbbZSmPqxOBhcU+a/PnRL8zVWJOpSCMLe04vrQNT 9lVXAxmcRZjq80fbfyjqomyaT5N5Yvvv6dIz2AtCnAOEynHLnJq/dd5eX840l3RC 1wXvNF4l0rzYm0yYHr0+oQXQ =iqXq -----END PGP SIGNATURE----- Merge tag 'rproc-v5.3' of git://github.com/andersson/remoteproc Pull remoteproc updates from Bjorn Andersson: "This adds support for the STM32 remoteproc, additional i.MX platforms with Cortex M4 remoteprocs and Qualcomm's QCS404 Compute DSP. Also initial support for vendor specific resource table entries and support for unprocessed Qualcomm firmware files" * tag 'rproc-v5.3' of git://github.com/andersson/remoteproc: remoteproc: stm32: fix building without ARM SMCC remoteproc: qcom: q6v5-mss: Fix build error without QCOM_MDT_LOADER remoteproc: copy parent dma_pfn_offset for vdev remoteproc: qcom: q6v5-mss: Support loading non-split images soc: qcom: mdt_loader: Support loading non-split images remoteproc: stm32: add an ST stm32_rproc driver dt-bindings: remoteproc: add bindings for stm32 remote processor driver dt-bindings: stm32: add bindings for ML-AHB interconnect remoteproc: Use struct_size() helper remoteproc: add vendor resources handling remoteproc: imx: Fix typo in "failed" remoteproc: imx: Broaden the Kconfig selection logic remoteproc,rpmsg: add missing MAINTAINERS file entries remoteproc: qcom: qdsp6-adsp: Add support for QCS404 CDSP dt-bindings: remoteproc: Rename and amend Hexagon v56 binding
422 lines
10 KiB
C
422 lines
10 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/err.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/remoteproc.h>
|
|
|
|
#define IMX7D_SRC_SCR 0x0C
|
|
#define IMX7D_ENABLE_M4 BIT(3)
|
|
#define IMX7D_SW_M4P_RST BIT(2)
|
|
#define IMX7D_SW_M4C_RST BIT(1)
|
|
#define IMX7D_SW_M4C_NON_SCLR_RST BIT(0)
|
|
|
|
#define IMX7D_M4_RST_MASK (IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \
|
|
| IMX7D_SW_M4C_RST \
|
|
| IMX7D_SW_M4C_NON_SCLR_RST)
|
|
|
|
#define IMX7D_M4_START (IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \
|
|
| IMX7D_SW_M4C_RST)
|
|
#define IMX7D_M4_STOP IMX7D_SW_M4C_NON_SCLR_RST
|
|
|
|
/* Address: 0x020D8000 */
|
|
#define IMX6SX_SRC_SCR 0x00
|
|
#define IMX6SX_ENABLE_M4 BIT(22)
|
|
#define IMX6SX_SW_M4P_RST BIT(12)
|
|
#define IMX6SX_SW_M4C_NON_SCLR_RST BIT(4)
|
|
#define IMX6SX_SW_M4C_RST BIT(3)
|
|
|
|
#define IMX6SX_M4_START (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \
|
|
| IMX6SX_SW_M4C_RST)
|
|
#define IMX6SX_M4_STOP IMX6SX_SW_M4C_NON_SCLR_RST
|
|
#define IMX6SX_M4_RST_MASK (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \
|
|
| IMX6SX_SW_M4C_NON_SCLR_RST \
|
|
| IMX6SX_SW_M4C_RST)
|
|
|
|
#define IMX7D_RPROC_MEM_MAX 8
|
|
|
|
/**
|
|
* struct imx_rproc_mem - slim internal memory structure
|
|
* @cpu_addr: MPU virtual address of the memory region
|
|
* @sys_addr: Bus address used to access the memory region
|
|
* @size: Size of the memory region
|
|
*/
|
|
struct imx_rproc_mem {
|
|
void __iomem *cpu_addr;
|
|
phys_addr_t sys_addr;
|
|
size_t size;
|
|
};
|
|
|
|
/* att flags */
|
|
/* M4 own area. Can be mapped at probe */
|
|
#define ATT_OWN BIT(1)
|
|
|
|
/* address translation table */
|
|
struct imx_rproc_att {
|
|
u32 da; /* device address (From Cortex M4 view)*/
|
|
u32 sa; /* system bus address */
|
|
u32 size; /* size of reg range */
|
|
int flags;
|
|
};
|
|
|
|
struct imx_rproc_dcfg {
|
|
u32 src_reg;
|
|
u32 src_mask;
|
|
u32 src_start;
|
|
u32 src_stop;
|
|
const struct imx_rproc_att *att;
|
|
size_t att_size;
|
|
};
|
|
|
|
struct imx_rproc {
|
|
struct device *dev;
|
|
struct regmap *regmap;
|
|
struct rproc *rproc;
|
|
const struct imx_rproc_dcfg *dcfg;
|
|
struct imx_rproc_mem mem[IMX7D_RPROC_MEM_MAX];
|
|
struct clk *clk;
|
|
};
|
|
|
|
static const struct imx_rproc_att imx_rproc_att_imx7d[] = {
|
|
/* dev addr , sys addr , size , flags */
|
|
/* OCRAM_S (M4 Boot code) - alias */
|
|
{ 0x00000000, 0x00180000, 0x00008000, 0 },
|
|
/* OCRAM_S (Code) */
|
|
{ 0x00180000, 0x00180000, 0x00008000, ATT_OWN },
|
|
/* OCRAM (Code) - alias */
|
|
{ 0x00900000, 0x00900000, 0x00020000, 0 },
|
|
/* OCRAM_EPDC (Code) - alias */
|
|
{ 0x00920000, 0x00920000, 0x00020000, 0 },
|
|
/* OCRAM_PXP (Code) - alias */
|
|
{ 0x00940000, 0x00940000, 0x00008000, 0 },
|
|
/* TCML (Code) */
|
|
{ 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN },
|
|
/* DDR (Code) - alias, first part of DDR (Data) */
|
|
{ 0x10000000, 0x80000000, 0x0FFF0000, 0 },
|
|
|
|
/* TCMU (Data) */
|
|
{ 0x20000000, 0x00800000, 0x00008000, ATT_OWN },
|
|
/* OCRAM (Data) */
|
|
{ 0x20200000, 0x00900000, 0x00020000, 0 },
|
|
/* OCRAM_EPDC (Data) */
|
|
{ 0x20220000, 0x00920000, 0x00020000, 0 },
|
|
/* OCRAM_PXP (Data) */
|
|
{ 0x20240000, 0x00940000, 0x00008000, 0 },
|
|
/* DDR (Data) */
|
|
{ 0x80000000, 0x80000000, 0x60000000, 0 },
|
|
};
|
|
|
|
static const struct imx_rproc_att imx_rproc_att_imx6sx[] = {
|
|
/* dev addr , sys addr , size , flags */
|
|
/* TCML (M4 Boot Code) - alias */
|
|
{ 0x00000000, 0x007F8000, 0x00008000, 0 },
|
|
/* OCRAM_S (Code) */
|
|
{ 0x00180000, 0x008F8000, 0x00004000, 0 },
|
|
/* OCRAM_S (Code) - alias */
|
|
{ 0x00180000, 0x008FC000, 0x00004000, 0 },
|
|
/* TCML (Code) */
|
|
{ 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN },
|
|
/* DDR (Code) - alias, first part of DDR (Data) */
|
|
{ 0x10000000, 0x80000000, 0x0FFF8000, 0 },
|
|
|
|
/* TCMU (Data) */
|
|
{ 0x20000000, 0x00800000, 0x00008000, ATT_OWN },
|
|
/* OCRAM_S (Data) - alias? */
|
|
{ 0x208F8000, 0x008F8000, 0x00004000, 0 },
|
|
/* DDR (Data) */
|
|
{ 0x80000000, 0x80000000, 0x60000000, 0 },
|
|
};
|
|
|
|
static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = {
|
|
.src_reg = IMX7D_SRC_SCR,
|
|
.src_mask = IMX7D_M4_RST_MASK,
|
|
.src_start = IMX7D_M4_START,
|
|
.src_stop = IMX7D_M4_STOP,
|
|
.att = imx_rproc_att_imx7d,
|
|
.att_size = ARRAY_SIZE(imx_rproc_att_imx7d),
|
|
};
|
|
|
|
static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = {
|
|
.src_reg = IMX6SX_SRC_SCR,
|
|
.src_mask = IMX6SX_M4_RST_MASK,
|
|
.src_start = IMX6SX_M4_START,
|
|
.src_stop = IMX6SX_M4_STOP,
|
|
.att = imx_rproc_att_imx6sx,
|
|
.att_size = ARRAY_SIZE(imx_rproc_att_imx6sx),
|
|
};
|
|
|
|
static int imx_rproc_start(struct rproc *rproc)
|
|
{
|
|
struct imx_rproc *priv = rproc->priv;
|
|
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
|
|
struct device *dev = priv->dev;
|
|
int ret;
|
|
|
|
ret = regmap_update_bits(priv->regmap, dcfg->src_reg,
|
|
dcfg->src_mask, dcfg->src_start);
|
|
if (ret)
|
|
dev_err(dev, "Failed to enable M4!\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int imx_rproc_stop(struct rproc *rproc)
|
|
{
|
|
struct imx_rproc *priv = rproc->priv;
|
|
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
|
|
struct device *dev = priv->dev;
|
|
int ret;
|
|
|
|
ret = regmap_update_bits(priv->regmap, dcfg->src_reg,
|
|
dcfg->src_mask, dcfg->src_stop);
|
|
if (ret)
|
|
dev_err(dev, "Failed to stop M4!\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da,
|
|
int len, u64 *sys)
|
|
{
|
|
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
|
|
int i;
|
|
|
|
/* parse address translation table */
|
|
for (i = 0; i < dcfg->att_size; i++) {
|
|
const struct imx_rproc_att *att = &dcfg->att[i];
|
|
|
|
if (da >= att->da && da + len < att->da + att->size) {
|
|
unsigned int offset = da - att->da;
|
|
|
|
*sys = att->sa + offset;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
dev_warn(priv->dev, "Translation failed: da = 0x%llx len = 0x%x\n",
|
|
da, len);
|
|
return -ENOENT;
|
|
}
|
|
|
|
static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
|
|
{
|
|
struct imx_rproc *priv = rproc->priv;
|
|
void *va = NULL;
|
|
u64 sys;
|
|
int i;
|
|
|
|
if (len <= 0)
|
|
return NULL;
|
|
|
|
/*
|
|
* On device side we have many aliases, so we need to convert device
|
|
* address (M4) to system bus address first.
|
|
*/
|
|
if (imx_rproc_da_to_sys(priv, da, len, &sys))
|
|
return NULL;
|
|
|
|
for (i = 0; i < IMX7D_RPROC_MEM_MAX; i++) {
|
|
if (sys >= priv->mem[i].sys_addr && sys + len <
|
|
priv->mem[i].sys_addr + priv->mem[i].size) {
|
|
unsigned int offset = sys - priv->mem[i].sys_addr;
|
|
/* __force to make sparse happy with type conversion */
|
|
va = (__force void *)(priv->mem[i].cpu_addr + offset);
|
|
break;
|
|
}
|
|
}
|
|
|
|
dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%p\n", da, len, va);
|
|
|
|
return va;
|
|
}
|
|
|
|
static const struct rproc_ops imx_rproc_ops = {
|
|
.start = imx_rproc_start,
|
|
.stop = imx_rproc_stop,
|
|
.da_to_va = imx_rproc_da_to_va,
|
|
};
|
|
|
|
static int imx_rproc_addr_init(struct imx_rproc *priv,
|
|
struct platform_device *pdev)
|
|
{
|
|
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
int a, b = 0, err, nph;
|
|
|
|
/* remap required addresses */
|
|
for (a = 0; a < dcfg->att_size; a++) {
|
|
const struct imx_rproc_att *att = &dcfg->att[a];
|
|
|
|
if (!(att->flags & ATT_OWN))
|
|
continue;
|
|
|
|
if (b >= IMX7D_RPROC_MEM_MAX)
|
|
break;
|
|
|
|
priv->mem[b].cpu_addr = devm_ioremap(&pdev->dev,
|
|
att->sa, att->size);
|
|
if (!priv->mem[b].cpu_addr) {
|
|
dev_err(dev, "devm_ioremap_resource failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
priv->mem[b].sys_addr = att->sa;
|
|
priv->mem[b].size = att->size;
|
|
b++;
|
|
}
|
|
|
|
/* memory-region is optional property */
|
|
nph = of_count_phandle_with_args(np, "memory-region", NULL);
|
|
if (nph <= 0)
|
|
return 0;
|
|
|
|
/* remap optional addresses */
|
|
for (a = 0; a < nph; a++) {
|
|
struct device_node *node;
|
|
struct resource res;
|
|
|
|
node = of_parse_phandle(np, "memory-region", a);
|
|
err = of_address_to_resource(node, 0, &res);
|
|
if (err) {
|
|
dev_err(dev, "unable to resolve memory region\n");
|
|
return err;
|
|
}
|
|
|
|
if (b >= IMX7D_RPROC_MEM_MAX)
|
|
break;
|
|
|
|
priv->mem[b].cpu_addr = devm_ioremap_resource(&pdev->dev, &res);
|
|
if (IS_ERR(priv->mem[b].cpu_addr)) {
|
|
dev_err(dev, "devm_ioremap_resource failed\n");
|
|
err = PTR_ERR(priv->mem[b].cpu_addr);
|
|
return err;
|
|
}
|
|
priv->mem[b].sys_addr = res.start;
|
|
priv->mem[b].size = resource_size(&res);
|
|
b++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int imx_rproc_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
struct imx_rproc *priv;
|
|
struct rproc *rproc;
|
|
struct regmap_config config = { .name = "imx-rproc" };
|
|
const struct imx_rproc_dcfg *dcfg;
|
|
struct regmap *regmap;
|
|
int ret;
|
|
|
|
regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
|
|
if (IS_ERR(regmap)) {
|
|
dev_err(dev, "failed to find syscon\n");
|
|
return PTR_ERR(regmap);
|
|
}
|
|
regmap_attach_dev(dev, regmap, &config);
|
|
|
|
/* set some other name then imx */
|
|
rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops,
|
|
NULL, sizeof(*priv));
|
|
if (!rproc)
|
|
return -ENOMEM;
|
|
|
|
dcfg = of_device_get_match_data(dev);
|
|
if (!dcfg) {
|
|
ret = -EINVAL;
|
|
goto err_put_rproc;
|
|
}
|
|
|
|
priv = rproc->priv;
|
|
priv->rproc = rproc;
|
|
priv->regmap = regmap;
|
|
priv->dcfg = dcfg;
|
|
priv->dev = dev;
|
|
|
|
dev_set_drvdata(dev, rproc);
|
|
|
|
ret = imx_rproc_addr_init(priv, pdev);
|
|
if (ret) {
|
|
dev_err(dev, "failed on imx_rproc_addr_init\n");
|
|
goto err_put_rproc;
|
|
}
|
|
|
|
priv->clk = devm_clk_get(dev, NULL);
|
|
if (IS_ERR(priv->clk)) {
|
|
dev_err(dev, "Failed to get clock\n");
|
|
ret = PTR_ERR(priv->clk);
|
|
goto err_put_rproc;
|
|
}
|
|
|
|
/*
|
|
* clk for M4 block including memory. Should be
|
|
* enabled before .start for FW transfer.
|
|
*/
|
|
ret = clk_prepare_enable(priv->clk);
|
|
if (ret) {
|
|
dev_err(&rproc->dev, "Failed to enable clock\n");
|
|
goto err_put_rproc;
|
|
}
|
|
|
|
ret = rproc_add(rproc);
|
|
if (ret) {
|
|
dev_err(dev, "rproc_add failed\n");
|
|
goto err_put_clk;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_put_clk:
|
|
clk_disable_unprepare(priv->clk);
|
|
err_put_rproc:
|
|
rproc_free(rproc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int imx_rproc_remove(struct platform_device *pdev)
|
|
{
|
|
struct rproc *rproc = platform_get_drvdata(pdev);
|
|
struct imx_rproc *priv = rproc->priv;
|
|
|
|
clk_disable_unprepare(priv->clk);
|
|
rproc_del(rproc);
|
|
rproc_free(rproc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id imx_rproc_of_match[] = {
|
|
{ .compatible = "fsl,imx7d-cm4", .data = &imx_rproc_cfg_imx7d },
|
|
{ .compatible = "fsl,imx6sx-cm4", .data = &imx_rproc_cfg_imx6sx },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, imx_rproc_of_match);
|
|
|
|
static struct platform_driver imx_rproc_driver = {
|
|
.probe = imx_rproc_probe,
|
|
.remove = imx_rproc_remove,
|
|
.driver = {
|
|
.name = "imx-rproc",
|
|
.of_match_table = imx_rproc_of_match,
|
|
},
|
|
};
|
|
|
|
module_platform_driver(imx_rproc_driver);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("IMX6SX/7D remote processor control driver");
|
|
MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
|