diff options
Diffstat (limited to 'drivers/remoteproc/imx_rproc.c')
| -rw-r--r-- | drivers/remoteproc/imx_rproc.c | 322 | 
1 files changed, 306 insertions, 16 deletions
| diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 8957ed271d20..d6338872c6db 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -7,13 +7,18 @@  #include <linux/err.h>  #include <linux/interrupt.h>  #include <linux/kernel.h> +#include <linux/mailbox_client.h>  #include <linux/mfd/syscon.h>  #include <linux/module.h>  #include <linux/of_address.h> +#include <linux/of_reserved_mem.h>  #include <linux/of_device.h>  #include <linux/platform_device.h>  #include <linux/regmap.h>  #include <linux/remoteproc.h> +#include <linux/workqueue.h> + +#include "remoteproc_internal.h"  #define IMX7D_SRC_SCR			0x0C  #define IMX7D_ENABLE_M4			BIT(3) @@ -43,7 +48,7 @@  					 | IMX6SX_SW_M4C_NON_SCLR_RST \  					 | IMX6SX_SW_M4C_RST) -#define IMX7D_RPROC_MEM_MAX		8 +#define IMX_RPROC_MEM_MAX		32  /**   * struct imx_rproc_mem - slim internal memory structure @@ -83,8 +88,42 @@ struct imx_rproc {  	struct regmap			*regmap;  	struct rproc			*rproc;  	const struct imx_rproc_dcfg	*dcfg; -	struct imx_rproc_mem		mem[IMX7D_RPROC_MEM_MAX]; +	struct imx_rproc_mem		mem[IMX_RPROC_MEM_MAX];  	struct clk			*clk; +	struct mbox_client		cl; +	struct mbox_chan		*tx_ch; +	struct mbox_chan		*rx_ch; +	struct work_struct		rproc_work; +	struct workqueue_struct		*workqueue; +	void __iomem			*rsc_table; +}; + +static const struct imx_rproc_att imx_rproc_att_imx8mq[] = { +	/* dev addr , sys addr  , size	    , flags */ +	/* TCML - alias */ +	{ 0x00000000, 0x007e0000, 0x00020000, 0 }, +	/* OCRAM_S */ +	{ 0x00180000, 0x00180000, 0x00008000, 0 }, +	/* OCRAM */ +	{ 0x00900000, 0x00900000, 0x00020000, 0 }, +	/* OCRAM */ +	{ 0x00920000, 0x00920000, 0x00020000, 0 }, +	/* QSPI Code - alias */ +	{ 0x08000000, 0x08000000, 0x08000000, 0 }, +	/* DDR (Code) - alias */ +	{ 0x10000000, 0x80000000, 0x0FFE0000, 0 }, +	/* TCML */ +	{ 0x1FFE0000, 0x007E0000, 0x00020000, ATT_OWN }, +	/* TCMU */ +	{ 0x20000000, 0x00800000, 0x00020000, ATT_OWN }, +	/* OCRAM_S */ +	{ 0x20180000, 0x00180000, 0x00008000, ATT_OWN }, +	/* OCRAM */ +	{ 0x20200000, 0x00900000, 0x00020000, ATT_OWN }, +	/* OCRAM */ +	{ 0x20220000, 0x00920000, 0x00020000, ATT_OWN }, +	/* DDR (Data) */ +	{ 0x40000000, 0x40000000, 0x80000000, 0 },  };  static const struct imx_rproc_att imx_rproc_att_imx7d[] = { @@ -137,6 +176,15 @@ static const struct imx_rproc_att imx_rproc_att_imx6sx[] = {  	{ 0x80000000, 0x80000000, 0x60000000, 0 },  }; +static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mq = { +	.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_imx8mq, +	.att_size	= ARRAY_SIZE(imx_rproc_att_imx8mq), +}; +  static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = {  	.src_reg	= IMX7D_SRC_SCR,  	.src_mask	= IMX7D_M4_RST_MASK, @@ -208,7 +256,7 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da,  	return -ENOENT;  } -static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) +static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)  {  	struct imx_rproc *priv = rproc->priv;  	void *va = NULL; @@ -225,7 +273,7 @@ static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)  	if (imx_rproc_da_to_sys(priv, da, len, &sys))  		return NULL; -	for (i = 0; i < IMX7D_RPROC_MEM_MAX; i++) { +	for (i = 0; i < IMX_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; @@ -241,10 +289,143 @@ static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len)  	return va;  } +static int imx_rproc_mem_alloc(struct rproc *rproc, +			       struct rproc_mem_entry *mem) +{ +	struct device *dev = rproc->dev.parent; +	void *va; + +	dev_dbg(dev, "map memory: %p+%zx\n", &mem->dma, mem->len); +	va = ioremap_wc(mem->dma, mem->len); +	if (IS_ERR_OR_NULL(va)) { +		dev_err(dev, "Unable to map memory region: %p+%zx\n", +			&mem->dma, mem->len); +		return -ENOMEM; +	} + +	/* Update memory entry va */ +	mem->va = va; + +	return 0; +} + +static int imx_rproc_mem_release(struct rproc *rproc, +				 struct rproc_mem_entry *mem) +{ +	dev_dbg(rproc->dev.parent, "unmap memory: %pa\n", &mem->dma); +	iounmap(mem->va); + +	return 0; +} + +static int imx_rproc_prepare(struct rproc *rproc) +{ +	struct imx_rproc *priv = rproc->priv; +	struct device_node *np = priv->dev->of_node; +	struct of_phandle_iterator it; +	struct rproc_mem_entry *mem; +	struct reserved_mem *rmem; +	u32 da; + +	/* Register associated reserved memory regions */ +	of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); +	while (of_phandle_iterator_next(&it) == 0) { +		/* +		 * Ignore the first memory region which will be used vdev buffer. +		 * No need to do extra handlings, rproc_add_virtio_dev will handle it. +		 */ +		if (!strcmp(it.node->name, "vdev0buffer")) +			continue; + +		rmem = of_reserved_mem_lookup(it.node); +		if (!rmem) { +			dev_err(priv->dev, "unable to acquire memory-region\n"); +			return -EINVAL; +		} + +		/* No need to translate pa to da, i.MX use same map */ +		da = rmem->base; + +		/* Register memory region */ +		mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)rmem->base, rmem->size, da, +					   imx_rproc_mem_alloc, imx_rproc_mem_release, +					   it.node->name); + +		if (mem) +			rproc_coredump_add_segment(rproc, da, rmem->size); +		else +			return -ENOMEM; + +		rproc_add_carveout(rproc, mem); +	} + +	return  0; +} + +static int imx_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{ +	int ret; + +	ret = rproc_elf_load_rsc_table(rproc, fw); +	if (ret) +		dev_info(&rproc->dev, "No resource table in elf\n"); + +	return 0; +} + +static void imx_rproc_kick(struct rproc *rproc, int vqid) +{ +	struct imx_rproc *priv = rproc->priv; +	int err; +	__u32 mmsg; + +	if (!priv->tx_ch) { +		dev_err(priv->dev, "No initialized mbox tx channel\n"); +		return; +	} + +	/* +	 * Send the index of the triggered virtqueue as the mu payload. +	 * Let remote processor know which virtqueue is used. +	 */ +	mmsg = vqid << 16; + +	err = mbox_send_message(priv->tx_ch, (void *)&mmsg); +	if (err < 0) +		dev_err(priv->dev, "%s: failed (%d, err:%d)\n", +			__func__, vqid, err); +} + +static int imx_rproc_attach(struct rproc *rproc) +{ +	return 0; +} + +static struct resource_table *imx_rproc_get_loaded_rsc_table(struct rproc *rproc, size_t *table_sz) +{ +	struct imx_rproc *priv = rproc->priv; + +	/* The resource table has already been mapped in imx_rproc_addr_init */ +	if (!priv->rsc_table) +		return NULL; + +	*table_sz = SZ_1K; +	return (struct resource_table *)priv->rsc_table; +} +  static const struct rproc_ops imx_rproc_ops = { +	.prepare	= imx_rproc_prepare, +	.attach		= imx_rproc_attach,  	.start		= imx_rproc_start,  	.stop		= imx_rproc_stop, +	.kick		= imx_rproc_kick,  	.da_to_va       = imx_rproc_da_to_va, +	.load		= rproc_elf_load_segments, +	.parse_fw	= imx_rproc_parse_fw, +	.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, +	.get_loaded_rsc_table = imx_rproc_get_loaded_rsc_table, +	.sanity_check	= rproc_elf_sanity_check, +	.get_boot_addr	= rproc_elf_get_boot_addr,  };  static int imx_rproc_addr_init(struct imx_rproc *priv, @@ -262,13 +443,13 @@ static int imx_rproc_addr_init(struct imx_rproc *priv,  		if (!(att->flags & ATT_OWN))  			continue; -		if (b >= IMX7D_RPROC_MEM_MAX) +		if (b >= IMX_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"); +			dev_err(dev, "failed to remap %#x bytes from %#x\n", att->size, att->sa);  			return -ENOMEM;  		}  		priv->mem[b].sys_addr = att->sa; @@ -287,29 +468,115 @@ static int imx_rproc_addr_init(struct imx_rproc *priv,  		struct resource res;  		node = of_parse_phandle(np, "memory-region", a); +		/* Not map vdev region */ +		if (!strcmp(node->name, "vdev")) +			continue;  		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) +		of_node_put(node); + +		if (b >= IMX_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; +		/* Not use resource version, because we might share region */ +		priv->mem[b].cpu_addr = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); +		if (!priv->mem[b].cpu_addr) { +			dev_err(dev, "failed to remap %pr\n", &res); +			return -ENOMEM;  		}  		priv->mem[b].sys_addr = res.start;  		priv->mem[b].size = resource_size(&res); +		if (!strcmp(node->name, "rsc_table")) +			priv->rsc_table = priv->mem[b].cpu_addr;  		b++;  	}  	return 0;  } +static void imx_rproc_vq_work(struct work_struct *work) +{ +	struct imx_rproc *priv = container_of(work, struct imx_rproc, +					      rproc_work); + +	rproc_vq_interrupt(priv->rproc, 0); +	rproc_vq_interrupt(priv->rproc, 1); +} + +static void imx_rproc_rx_callback(struct mbox_client *cl, void *msg) +{ +	struct rproc *rproc = dev_get_drvdata(cl->dev); +	struct imx_rproc *priv = rproc->priv; + +	queue_work(priv->workqueue, &priv->rproc_work); +} + +static int imx_rproc_xtr_mbox_init(struct rproc *rproc) +{ +	struct imx_rproc *priv = rproc->priv; +	struct device *dev = priv->dev; +	struct mbox_client *cl; +	int ret; + +	if (!of_get_property(dev->of_node, "mbox-names", NULL)) +		return 0; + +	cl = &priv->cl; +	cl->dev = dev; +	cl->tx_block = true; +	cl->tx_tout = 100; +	cl->knows_txdone = false; +	cl->rx_callback = imx_rproc_rx_callback; + +	priv->tx_ch = mbox_request_channel_byname(cl, "tx"); +	if (IS_ERR(priv->tx_ch)) { +		ret = PTR_ERR(priv->tx_ch); +		return dev_err_probe(cl->dev, ret, +				     "failed to request tx mailbox channel: %d\n", ret); +	} + +	priv->rx_ch = mbox_request_channel_byname(cl, "rx"); +	if (IS_ERR(priv->rx_ch)) { +		mbox_free_channel(priv->tx_ch); +		ret = PTR_ERR(priv->rx_ch); +		return dev_err_probe(cl->dev, ret, +				     "failed to request rx mailbox channel: %d\n", ret); +	} + +	return 0; +} + +static void imx_rproc_free_mbox(struct rproc *rproc) +{ +	struct imx_rproc *priv = rproc->priv; + +	mbox_free_channel(priv->tx_ch); +	mbox_free_channel(priv->rx_ch); +} + +static int imx_rproc_detect_mode(struct imx_rproc *priv) +{ +	const struct imx_rproc_dcfg *dcfg = priv->dcfg; +	struct device *dev = priv->dev; +	int ret; +	u32 val; + +	ret = regmap_read(priv->regmap, dcfg->src_reg, &val); +	if (ret) { +		dev_err(dev, "Failed to read src\n"); +		return ret; +	} + +	if (!(val & dcfg->src_stop)) +		priv->rproc->state = RPROC_DETACHED; + +	return 0; +} +  static int imx_rproc_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; @@ -347,18 +614,32 @@ static int imx_rproc_probe(struct platform_device *pdev)  	priv->dev = dev;  	dev_set_drvdata(dev, rproc); +	priv->workqueue = create_workqueue(dev_name(dev)); +	if (!priv->workqueue) { +		dev_err(dev, "cannot create workqueue\n"); +		ret = -ENOMEM; +		goto err_put_rproc; +	} + +	ret = imx_rproc_xtr_mbox_init(rproc); +	if (ret) +		goto err_put_wkq;  	ret = imx_rproc_addr_init(priv, pdev);  	if (ret) {  		dev_err(dev, "failed on imx_rproc_addr_init\n"); -		goto err_put_rproc; +		goto err_put_mbox;  	} +	ret = imx_rproc_detect_mode(priv); +	if (ret) +		goto err_put_mbox; +  	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; +		goto err_put_mbox;  	}  	/* @@ -368,9 +649,11 @@ static int imx_rproc_probe(struct platform_device *pdev)  	ret = clk_prepare_enable(priv->clk);  	if (ret) {  		dev_err(&rproc->dev, "Failed to enable clock\n"); -		goto err_put_rproc; +		goto err_put_mbox;  	} +	INIT_WORK(&priv->rproc_work, imx_rproc_vq_work); +  	ret = rproc_add(rproc);  	if (ret) {  		dev_err(dev, "rproc_add failed\n"); @@ -381,6 +664,10 @@ static int imx_rproc_probe(struct platform_device *pdev)  err_put_clk:  	clk_disable_unprepare(priv->clk); +err_put_mbox: +	imx_rproc_free_mbox(rproc); +err_put_wkq: +	destroy_workqueue(priv->workqueue);  err_put_rproc:  	rproc_free(rproc); @@ -394,6 +681,7 @@ static int imx_rproc_remove(struct platform_device *pdev)  	clk_disable_unprepare(priv->clk);  	rproc_del(rproc); +	imx_rproc_free_mbox(rproc);  	rproc_free(rproc);  	return 0; @@ -402,6 +690,8 @@ static int imx_rproc_remove(struct platform_device *pdev)  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 }, +	{ .compatible = "fsl,imx8mq-cm4", .data = &imx_rproc_cfg_imx8mq }, +	{ .compatible = "fsl,imx8mm-cm4", .data = &imx_rproc_cfg_imx8mq },  	{},  };  MODULE_DEVICE_TABLE(of, imx_rproc_of_match); @@ -418,5 +708,5 @@ static struct platform_driver imx_rproc_driver = {  module_platform_driver(imx_rproc_driver);  MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("IMX6SX/7D remote processor control driver"); +MODULE_DESCRIPTION("i.MX remote processor control driver");  MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); | 
