linux-imx/drivers/remoteproc/imx_isp_rproc.c
Robert Chiras 5e5c8fbf17 LF-11070-5: remoteproc: i.MX95 driver for ISP-FW running on Remote Processor
This driver handles the ISP-FW by loading it into OCRAM region of the
Remote Processor, configure the OCRAM and booting the core.

Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
Reviewed-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
2024-04-03 10:00:36 +03:00

412 lines
9.3 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2024 NXP
* Author: Robert Chiras <robert.chiras@nxp.com>
*
* Remoteproc driver for the ISP FW running on M0+ core.
*
*/
#include <linux/clk.h>
#include <linux/firmware.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/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/remoteproc.h>
#include "imx_rproc.h"
#define IMX95_CAM_BLKCTL_REG_CM0P_ADDR_OFFSET1 0x300
#define IMX95_CAM_BLKCTL_REG_CM0P_ADDR_OFFSET2 0x304
#define IMX95_CAM_BLKCTL_REG_CM0P_CPUWAIT 0x308
#define IMX95_CAM_BLKCTL_REG_CM0P_CTL 0x30c
#define IMX95_CAM_BLKCTL_REG_CM0P_STAT 0x310
#define IMX95_CAM_BLKCTL_CM0P_CPUWAIT_CPW BIT(0)
#define IMX95_CAM_BLKCTL_CM0P_CPUWAIT_RST BIT(1)
#define IMX95_CAMID_CSR_REG_SRAMCTL_RAMCR 0x0
#define IMX95_CAMID_CSR_REG_SRAMCTL_RAMIAS 0x4
#define IMX95_CAMID_CSR_REG_SRAMCTL_RAMIAE 0x8
#define IMX95_CAMID_CSR_REG_SRAMCTL_RAMSR 0xc
#define IMX95_CAMID_CSR_RAMSR_IDONE BIT(0)
static const struct reg_field ocram_done =
REG_FIELD(IMX95_CAMID_CSR_REG_SRAMCTL_RAMSR, 0, 0);
#define WAIT_MAX_RETRIES 100
struct imx_isp_rproc {
struct rproc *rproc;
struct device *dev;
struct regmap *blk_ctl;
struct regmap *ocram_cfg;
const struct imx_isp_rproc_dcfg *cfg;
phys_addr_t mba_phys;
size_t mba_size;
bool mba_init;
};
/* Custom registers to initialize */
struct imx_isp_dcfg {
u32 src_reg;
u32 src_mask;
u32 src_val;
};
struct imx_isp_rproc_dcfg {
const struct imx_rproc_dcfg *dcfg;
const struct imx_isp_dcfg isp_dcfg[2];
};
static const struct imx_rproc_dcfg isp_rproc_cfg_imx95 = {
.src_reg = IMX95_CAM_BLKCTL_REG_CM0P_CPUWAIT,
.src_mask = IMX95_CAM_BLKCTL_CM0P_CPUWAIT_CPW |
IMX95_CAM_BLKCTL_CM0P_CPUWAIT_RST,
.src_start = IMX95_CAM_BLKCTL_CM0P_CPUWAIT_RST,
.src_stop = IMX95_CAM_BLKCTL_CM0P_CPUWAIT_CPW,
.method = IMX_RPROC_MMIO,
};
static const struct imx_isp_rproc_dcfg imx_isp_rproc_cfg_imx95 = {
.dcfg = &isp_rproc_cfg_imx95,
.isp_dcfg = {
{
.src_reg = IMX95_CAM_BLKCTL_REG_CM0P_ADDR_OFFSET1,
.src_mask = 0xFFFFFF00,
.src_val = 0X80000000
},
{
.src_reg = IMX95_CAM_BLKCTL_REG_CM0P_ADDR_OFFSET2,
.src_mask = 0xFFFFFF00,
.src_val = 0X20000000
},
}
};
static int imx_isp_start(struct rproc *rproc)
{
struct imx_isp_rproc *isp = (struct imx_isp_rproc *)rproc->priv;
const struct imx_isp_rproc_dcfg *cfg = isp->cfg;
const struct imx_rproc_dcfg *dcfg = cfg->dcfg;
int i;
int ret = 0;
for (i = 0; i < ARRAY_SIZE(cfg->isp_dcfg); i++) {
const struct imx_isp_dcfg *isp_cfg = &cfg->isp_dcfg[i];
if (!isp_cfg->src_reg)
continue;
ret |= regmap_update_bits(isp->blk_ctl,
isp_cfg->src_reg,
isp_cfg->src_mask,
isp_cfg->src_val);
}
/* Start the processor */
ret |= regmap_update_bits(isp->blk_ctl,
dcfg->src_reg,
dcfg->src_mask,
IMX95_CAM_BLKCTL_CM0P_CPUWAIT_CPW |
IMX95_CAM_BLKCTL_CM0P_CPUWAIT_RST);
ret |= regmap_update_bits(isp->blk_ctl,
dcfg->src_reg,
dcfg->src_mask,
dcfg->src_start);
if (ret)
dev_err(isp->dev, "Failed to enable remote core!\n");
return ret;
}
static int imx_isp_stop(struct rproc *rproc)
{
struct imx_isp_rproc *isp = (struct imx_isp_rproc *)rproc->priv;
const struct imx_isp_rproc_dcfg *cfg = isp->cfg;
const struct imx_rproc_dcfg *dcfg = cfg->dcfg;
int ret;
/* Stop the processor */
ret = regmap_update_bits(isp->blk_ctl,
dcfg->src_reg,
dcfg->src_mask,
dcfg->src_stop);
isp->mba_init = false;
if (ret)
dev_err(isp->dev, "Failed to disable remote core!\n");
return ret;
}
static int imx_isp_alloc_memory_region(struct imx_isp_rproc *isp)
{
struct device_node *node;
struct device_node *child;
struct resource r;
unsigned int map[2] = {0};
int ret = 0;
int i;
struct regmap_field *field;
u32 init_done = 0;
child = of_get_child_by_name(isp->dev->of_node, "mba");
if (!child) {
node = of_parse_phandle(isp->dev->of_node,
"memory-region", 0);
} else {
node = of_parse_phandle(child, "memory-region", 0);
of_property_read_variable_u32_array(child, "ocram-map",
map, 0, ARRAY_SIZE(map));
of_node_put(child);
}
ret = of_address_to_resource(node, 0, &r);
of_node_put(node);
if (ret) {
dev_err(isp->dev, "unable to resolve MBA region\n");
return ret;
}
isp->mba_phys = r.start;
isp->mba_size = resource_size(&r);
if (!map[0])
map[0] = 0;
if (!map[1])
map[1] = isp->mba_size;
/* Stop the processor, if running */
imx_isp_stop(isp->rproc);
/* Initialize OCRAM */
dev_info(isp->dev, "OCRAM init: %pa+%zx", &isp->mba_phys, isp->mba_size);
field = devm_regmap_field_alloc(isp->dev, isp->ocram_cfg, ocram_done);
if (IS_ERR(field)) {
dev_err(isp->dev, "ocram_done field alloc failed");
return PTR_ERR(field);
}
ret = regmap_update_bits(isp->ocram_cfg,
IMX95_CAMID_CSR_REG_SRAMCTL_RAMIAS,
~0,
map[0]);
ret |= regmap_update_bits(isp->ocram_cfg,
IMX95_CAMID_CSR_REG_SRAMCTL_RAMIAE,
~0,
map[1]);
/* Request initialization */
ret |= regmap_update_bits(isp->ocram_cfg,
IMX95_CAMID_CSR_REG_SRAMCTL_RAMCR,
BIT(0),
BIT(0));
if (ret) {
dev_err(isp->dev, "OCRAM init map failed: %d", ret);
return ret;
}
for (i = 0; i < WAIT_MAX_RETRIES; i++) {
ret = regmap_field_read(field, &init_done);
if (ret)
return ret;
if (init_done)
break;
usleep_range(100, 200);
}
if (!init_done) {
dev_err(isp->dev, "OCRAM initialization failed\n");
return -EFAULT;
}
regmap_update_bits(isp->ocram_cfg,
IMX95_CAMID_CSR_REG_SRAMCTL_RAMSR,
BIT(0),
BIT(0));
dev_info(isp->dev, "OCRAM mapped at: %u+%x", map[0], map[1]);
isp->mba_init = true;
return ret;
}
static int imx_isp_load(struct rproc *rproc, const struct firmware *fw)
{
struct imx_isp_rproc *isp = rproc->priv;
void *mba_region;
int ret = 0;
if (!isp->mba_init) {
ret = imx_isp_alloc_memory_region(isp);
if (ret)
return ret;
}
if (fw->size > isp->mba_size) {
dev_err(isp->dev, "ISP firmware file too big: %lu > %lu",
fw->size, isp->mba_size);
return -EINVAL;
}
mba_region = memremap(isp->mba_phys, isp->mba_size, MEMREMAP_WC);
if (!mba_region) {
dev_err(isp->dev, "unable to map memory region: %pa+%zx\n",
&isp->mba_phys, isp->mba_size);
return -EBUSY;
}
memcpy(mba_region, fw->data, fw->size);
memunmap(mba_region);
dev_info(isp->dev, "ISP firmware (%lu bytes) loaded to: %pa+%zx",
fw->size, &isp->mba_phys, isp->mba_size);
return 0;
}
static const struct rproc_ops imx_isp_rproc_ops = {
.start = imx_isp_start,
.stop = imx_isp_stop,
.load = imx_isp_load,
};
static int imx_isp_rproc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
const char *fw_name;
const struct imx_isp_rproc_dcfg *isp_cfg;
struct imx_isp_rproc *isp_rproc;
struct rproc *rproc;
struct clk *clk_m0;
int ret;
isp_cfg = of_device_get_match_data(dev);
if (!isp_cfg)
return -ENODEV;
ret = of_property_read_string(np, "nxp,isp-firmware",
&fw_name);
if (ret) {
dev_err(dev, "No firmware filename given\n");
return -ENODEV;
}
clk_m0 = devm_clk_get(dev, NULL);
if (IS_ERR(clk_m0))
return dev_err_probe(dev, PTR_ERR(clk_m0),
"failed to get M0 clock\n");
pm_runtime_enable(dev);
pm_runtime_resume_and_get(dev);
ret = clk_prepare_enable(clk_m0);
if (ret) {
dev_err(dev, "failed to enable M0 clock: %d\n", ret);
return ret;
}
rproc = rproc_alloc(dev, "imx-isp-rproc", &imx_isp_rproc_ops,
fw_name, sizeof(*isp_rproc));
if (!rproc) {
ret = -ENOMEM;
goto err_clk;
}
rproc->auto_boot = false;
isp_rproc = (struct imx_isp_rproc *)rproc->priv;
isp_rproc->rproc = rproc;
isp_rproc->cfg = isp_cfg;
isp_rproc->dev = &pdev->dev;
isp_rproc->blk_ctl = syscon_regmap_lookup_by_phandle(np, "nxp,blk-ctrl");
if (IS_ERR(isp_rproc->blk_ctl)) {
ret = PTR_ERR(isp_rproc->blk_ctl);
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get blk-ctrl: %d\n",
ret);
goto err_clk;
}
isp_rproc->ocram_cfg = syscon_regmap_lookup_by_phandle(np, "nxp,ocram-cfg");
if (IS_ERR(isp_rproc->ocram_cfg)) {
ret = PTR_ERR(isp_rproc->ocram_cfg);
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get camid-csr: %d\n",
ret);
goto err_clk;
}
ret = imx_isp_alloc_memory_region(isp_rproc);
if (ret)
goto err_clk;
dev_set_drvdata(dev, rproc);
ret = rproc_add(rproc);
if (ret) {
dev_err(dev, "rproc_add failed\n");
rproc_free(rproc);
goto err_clk;
}
return 0;
err_clk:
clk_disable_unprepare(clk_m0);
return ret;
};
static int imx_isp_rproc_remove(struct platform_device *pdev)
{
struct rproc *rproc = platform_get_drvdata(pdev);
rproc_del(rproc);
rproc_free(rproc);
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct of_device_id imx_isp_rproc_of_match[] = {
{ .compatible = "nxp,imx95-isp-rproc",
.data = &imx_isp_rproc_cfg_imx95,
},
{},
};
MODULE_DEVICE_TABLE(of, imx_isp_rproc_of_match);
static struct platform_driver imx_isp_rproc_driver = {
.probe = imx_isp_rproc_probe,
.remove = imx_isp_rproc_remove,
.driver = {
.name = "imx-isp-rproc",
.of_match_table = imx_isp_rproc_of_match,
},
};
module_platform_driver(imx_isp_rproc_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("i.MX ISP Core Remote Processor Control Driver");
MODULE_AUTHOR("Robert Chiras <robert.chiras@nxp.com>");