Files
android_kernel_xiaomi_sm8450/drivers/remoteproc/imx_rproc.c
Linus Torvalds fdcec00405 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
2019-07-17 11:44:41 -07:00

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>");