162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SLIM core rproc driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016 STMicroelectronics
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Peter Griffin <peter.griffin@linaro.org>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/remoteproc.h>
1762306a36Sopenharmony_ci#include <linux/remoteproc/st_slim_rproc.h>
1862306a36Sopenharmony_ci#include "remoteproc_internal.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* SLIM core registers */
2162306a36Sopenharmony_ci#define SLIM_ID_OFST		0x0
2262306a36Sopenharmony_ci#define SLIM_VER_OFST		0x4
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define SLIM_EN_OFST		0x8
2562306a36Sopenharmony_ci#define SLIM_EN_RUN			BIT(0)
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define SLIM_CLK_GATE_OFST	0xC
2862306a36Sopenharmony_ci#define SLIM_CLK_GATE_DIS		BIT(0)
2962306a36Sopenharmony_ci#define SLIM_CLK_GATE_RESET		BIT(2)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define SLIM_SLIM_PC_OFST	0x20
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* DMEM registers */
3462306a36Sopenharmony_ci#define SLIM_REV_ID_OFST	0x0
3562306a36Sopenharmony_ci#define SLIM_REV_ID_MIN_MASK		GENMASK(15, 8)
3662306a36Sopenharmony_ci#define SLIM_REV_ID_MIN(id)		((id & SLIM_REV_ID_MIN_MASK) >> 8)
3762306a36Sopenharmony_ci#define SLIM_REV_ID_MAJ_MASK		GENMASK(23, 16)
3862306a36Sopenharmony_ci#define SLIM_REV_ID_MAJ(id)		((id & SLIM_REV_ID_MAJ_MASK) >> 16)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* peripherals registers */
4262306a36Sopenharmony_ci#define SLIM_STBUS_SYNC_OFST	0xF88
4362306a36Sopenharmony_ci#define SLIM_STBUS_SYNC_DIS		BIT(0)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define SLIM_INT_SET_OFST	0xFD4
4662306a36Sopenharmony_ci#define SLIM_INT_CLR_OFST	0xFD8
4762306a36Sopenharmony_ci#define SLIM_INT_MASK_OFST	0xFDC
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define SLIM_CMD_CLR_OFST	0xFC8
5062306a36Sopenharmony_ci#define SLIM_CMD_MASK_OFST	0xFCC
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic const char *mem_names[ST_SLIM_MEM_MAX] = {
5362306a36Sopenharmony_ci	[ST_SLIM_DMEM]	= "dmem",
5462306a36Sopenharmony_ci	[ST_SLIM_IMEM]	= "imem",
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic int slim_clk_get(struct st_slim_rproc *slim_rproc, struct device *dev)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	int clk, err;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	for (clk = 0; clk < ST_SLIM_MAX_CLK; clk++) {
6262306a36Sopenharmony_ci		slim_rproc->clks[clk] = of_clk_get(dev->of_node, clk);
6362306a36Sopenharmony_ci		if (IS_ERR(slim_rproc->clks[clk])) {
6462306a36Sopenharmony_ci			err = PTR_ERR(slim_rproc->clks[clk]);
6562306a36Sopenharmony_ci			if (err == -EPROBE_DEFER)
6662306a36Sopenharmony_ci				goto err_put_clks;
6762306a36Sopenharmony_ci			slim_rproc->clks[clk] = NULL;
6862306a36Sopenharmony_ci			break;
6962306a36Sopenharmony_ci		}
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return 0;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cierr_put_clks:
7562306a36Sopenharmony_ci	while (--clk >= 0)
7662306a36Sopenharmony_ci		clk_put(slim_rproc->clks[clk]);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return err;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic void slim_clk_disable(struct st_slim_rproc *slim_rproc)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	int clk;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++)
8662306a36Sopenharmony_ci		clk_disable_unprepare(slim_rproc->clks[clk]);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int slim_clk_enable(struct st_slim_rproc *slim_rproc)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	int clk, ret;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++) {
9462306a36Sopenharmony_ci		ret = clk_prepare_enable(slim_rproc->clks[clk]);
9562306a36Sopenharmony_ci		if (ret)
9662306a36Sopenharmony_ci			goto err_disable_clks;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return 0;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cierr_disable_clks:
10262306a36Sopenharmony_ci	while (--clk >= 0)
10362306a36Sopenharmony_ci		clk_disable_unprepare(slim_rproc->clks[clk]);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return ret;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ci * Remoteproc slim specific device handlers
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistatic int slim_rproc_start(struct rproc *rproc)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
11462306a36Sopenharmony_ci	struct st_slim_rproc *slim_rproc = rproc->priv;
11562306a36Sopenharmony_ci	unsigned long hw_id, hw_ver, fw_rev;
11662306a36Sopenharmony_ci	u32 val;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* disable CPU pipeline clock & reset CPU pipeline */
11962306a36Sopenharmony_ci	val = SLIM_CLK_GATE_DIS | SLIM_CLK_GATE_RESET;
12062306a36Sopenharmony_ci	writel(val, slim_rproc->slimcore + SLIM_CLK_GATE_OFST);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* disable SLIM core STBus sync */
12362306a36Sopenharmony_ci	writel(SLIM_STBUS_SYNC_DIS, slim_rproc->peri + SLIM_STBUS_SYNC_OFST);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* enable cpu pipeline clock */
12662306a36Sopenharmony_ci	writel(!SLIM_CLK_GATE_DIS,
12762306a36Sopenharmony_ci		slim_rproc->slimcore + SLIM_CLK_GATE_OFST);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* clear int & cmd mailbox */
13062306a36Sopenharmony_ci	writel(~0U, slim_rproc->peri + SLIM_INT_CLR_OFST);
13162306a36Sopenharmony_ci	writel(~0U, slim_rproc->peri + SLIM_CMD_CLR_OFST);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* enable all channels cmd & int */
13462306a36Sopenharmony_ci	writel(~0U, slim_rproc->peri + SLIM_INT_MASK_OFST);
13562306a36Sopenharmony_ci	writel(~0U, slim_rproc->peri + SLIM_CMD_MASK_OFST);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* enable cpu */
13862306a36Sopenharmony_ci	writel(SLIM_EN_RUN, slim_rproc->slimcore + SLIM_EN_OFST);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	hw_id = readl_relaxed(slim_rproc->slimcore + SLIM_ID_OFST);
14162306a36Sopenharmony_ci	hw_ver = readl_relaxed(slim_rproc->slimcore + SLIM_VER_OFST);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	fw_rev = readl(slim_rproc->mem[ST_SLIM_DMEM].cpu_addr +
14462306a36Sopenharmony_ci			SLIM_REV_ID_OFST);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	dev_info(dev, "fw rev:%ld.%ld on SLIM %ld.%ld\n",
14762306a36Sopenharmony_ci		 SLIM_REV_ID_MAJ(fw_rev), SLIM_REV_ID_MIN(fw_rev),
14862306a36Sopenharmony_ci		 hw_id, hw_ver);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return 0;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic int slim_rproc_stop(struct rproc *rproc)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct st_slim_rproc *slim_rproc = rproc->priv;
15662306a36Sopenharmony_ci	u32 val;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* mask all (cmd & int) channels */
15962306a36Sopenharmony_ci	writel(0UL, slim_rproc->peri + SLIM_INT_MASK_OFST);
16062306a36Sopenharmony_ci	writel(0UL, slim_rproc->peri + SLIM_CMD_MASK_OFST);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* disable cpu pipeline clock */
16362306a36Sopenharmony_ci	writel(SLIM_CLK_GATE_DIS, slim_rproc->slimcore + SLIM_CLK_GATE_OFST);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	writel(!SLIM_EN_RUN, slim_rproc->slimcore + SLIM_EN_OFST);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	val = readl(slim_rproc->slimcore + SLIM_EN_OFST);
16862306a36Sopenharmony_ci	if (val & SLIM_EN_RUN)
16962306a36Sopenharmony_ci		dev_warn(&rproc->dev, "Failed to disable SLIM");
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	dev_dbg(&rproc->dev, "slim stopped\n");
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return 0;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct st_slim_rproc *slim_rproc = rproc->priv;
17962306a36Sopenharmony_ci	void *va = NULL;
18062306a36Sopenharmony_ci	int i;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	for (i = 0; i < ST_SLIM_MEM_MAX; i++) {
18362306a36Sopenharmony_ci		if (da != slim_rproc->mem[i].bus_addr)
18462306a36Sopenharmony_ci			continue;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		if (len <= slim_rproc->mem[i].size) {
18762306a36Sopenharmony_ci			/* __force to make sparse happy with type conversion */
18862306a36Sopenharmony_ci			va = (__force void *)slim_rproc->mem[i].cpu_addr;
18962306a36Sopenharmony_ci			break;
19062306a36Sopenharmony_ci		}
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%zx va = 0x%pK\n",
19462306a36Sopenharmony_ci		da, len, va);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return va;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic const struct rproc_ops slim_rproc_ops = {
20062306a36Sopenharmony_ci	.start		= slim_rproc_start,
20162306a36Sopenharmony_ci	.stop		= slim_rproc_stop,
20262306a36Sopenharmony_ci	.da_to_va       = slim_rproc_da_to_va,
20362306a36Sopenharmony_ci	.get_boot_addr	= rproc_elf_get_boot_addr,
20462306a36Sopenharmony_ci	.load		= rproc_elf_load_segments,
20562306a36Sopenharmony_ci	.sanity_check	= rproc_elf_sanity_check,
20662306a36Sopenharmony_ci};
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/**
20962306a36Sopenharmony_ci * st_slim_rproc_alloc() - allocate and initialise slim rproc
21062306a36Sopenharmony_ci * @pdev: Pointer to the platform_device struct
21162306a36Sopenharmony_ci * @fw_name: Name of firmware for rproc to use
21262306a36Sopenharmony_ci *
21362306a36Sopenharmony_ci * Function for allocating and initialising a slim rproc for use by
21462306a36Sopenharmony_ci * device drivers whose IP is based around the SLIM core. It
21562306a36Sopenharmony_ci * obtains and enables any clocks required by the SLIM core and also
21662306a36Sopenharmony_ci * ioremaps the various IO.
21762306a36Sopenharmony_ci *
21862306a36Sopenharmony_ci * Return: st_slim_rproc pointer or PTR_ERR() on error.
21962306a36Sopenharmony_ci */
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistruct st_slim_rproc *st_slim_rproc_alloc(struct platform_device *pdev,
22262306a36Sopenharmony_ci				char *fw_name)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
22562306a36Sopenharmony_ci	struct st_slim_rproc *slim_rproc;
22662306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
22762306a36Sopenharmony_ci	struct rproc *rproc;
22862306a36Sopenharmony_ci	struct resource *res;
22962306a36Sopenharmony_ci	int err, i;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (!fw_name)
23262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (!of_device_is_compatible(np, "st,slim-rproc"))
23562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	rproc = rproc_alloc(dev, np->name, &slim_rproc_ops,
23862306a36Sopenharmony_ci			fw_name, sizeof(*slim_rproc));
23962306a36Sopenharmony_ci	if (!rproc)
24062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	rproc->has_iommu = false;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	slim_rproc = rproc->priv;
24562306a36Sopenharmony_ci	slim_rproc->rproc = rproc;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* get imem and dmem */
24862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mem_names); i++) {
24962306a36Sopenharmony_ci		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
25062306a36Sopenharmony_ci						mem_names[i]);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		slim_rproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res);
25362306a36Sopenharmony_ci		if (IS_ERR(slim_rproc->mem[i].cpu_addr)) {
25462306a36Sopenharmony_ci			dev_err(&pdev->dev, "devm_ioremap_resource failed\n");
25562306a36Sopenharmony_ci			err = PTR_ERR(slim_rproc->mem[i].cpu_addr);
25662306a36Sopenharmony_ci			goto err;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci		slim_rproc->mem[i].bus_addr = res->start;
25962306a36Sopenharmony_ci		slim_rproc->mem[i].size = resource_size(res);
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "slimcore");
26362306a36Sopenharmony_ci	slim_rproc->slimcore = devm_ioremap_resource(dev, res);
26462306a36Sopenharmony_ci	if (IS_ERR(slim_rproc->slimcore)) {
26562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap slimcore IO\n");
26662306a36Sopenharmony_ci		err = PTR_ERR(slim_rproc->slimcore);
26762306a36Sopenharmony_ci		goto err;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "peripherals");
27162306a36Sopenharmony_ci	slim_rproc->peri = devm_ioremap_resource(dev, res);
27262306a36Sopenharmony_ci	if (IS_ERR(slim_rproc->peri)) {
27362306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap peripherals IO\n");
27462306a36Sopenharmony_ci		err = PTR_ERR(slim_rproc->peri);
27562306a36Sopenharmony_ci		goto err;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	err = slim_clk_get(slim_rproc, dev);
27962306a36Sopenharmony_ci	if (err)
28062306a36Sopenharmony_ci		goto err;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	err = slim_clk_enable(slim_rproc);
28362306a36Sopenharmony_ci	if (err) {
28462306a36Sopenharmony_ci		dev_err(dev, "Failed to enable clocks\n");
28562306a36Sopenharmony_ci		goto err_clk_put;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* Register as a remoteproc device */
28962306a36Sopenharmony_ci	err = rproc_add(rproc);
29062306a36Sopenharmony_ci	if (err) {
29162306a36Sopenharmony_ci		dev_err(dev, "registration of slim remoteproc failed\n");
29262306a36Sopenharmony_ci		goto err_clk_dis;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return slim_rproc;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cierr_clk_dis:
29862306a36Sopenharmony_ci	slim_clk_disable(slim_rproc);
29962306a36Sopenharmony_cierr_clk_put:
30062306a36Sopenharmony_ci	for (i = 0; i < ST_SLIM_MAX_CLK && slim_rproc->clks[i]; i++)
30162306a36Sopenharmony_ci		clk_put(slim_rproc->clks[i]);
30262306a36Sopenharmony_cierr:
30362306a36Sopenharmony_ci	rproc_free(rproc);
30462306a36Sopenharmony_ci	return ERR_PTR(err);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ciEXPORT_SYMBOL(st_slim_rproc_alloc);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci/**
30962306a36Sopenharmony_ci  * st_slim_rproc_put() - put slim rproc resources
31062306a36Sopenharmony_ci  * @slim_rproc: Pointer to the st_slim_rproc struct
31162306a36Sopenharmony_ci  *
31262306a36Sopenharmony_ci  * Function for calling respective _put() functions on slim_rproc resources.
31362306a36Sopenharmony_ci  *
31462306a36Sopenharmony_ci  */
31562306a36Sopenharmony_civoid st_slim_rproc_put(struct st_slim_rproc *slim_rproc)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	int clk;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (!slim_rproc)
32062306a36Sopenharmony_ci		return;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	slim_clk_disable(slim_rproc);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++)
32562306a36Sopenharmony_ci		clk_put(slim_rproc->clks[clk]);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	rproc_del(slim_rproc->rproc);
32862306a36Sopenharmony_ci	rproc_free(slim_rproc->rproc);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ciEXPORT_SYMBOL(st_slim_rproc_put);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ciMODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>");
33362306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics SLIM core rproc driver");
33462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
335