Re: [PATCH v20 2/4] remoteproc/mediatek: add SCP support for mt8183

From: Bjorn Andersson
Date: Mon Nov 11 2019 - 17:53:48 EST


On Mon 14 Oct 00:58 PDT 2019, Pi-Hsun Shih wrote:
> diff --git a/drivers/remoteproc/mtk_common.h b/drivers/remoteproc/mtk_common.h
[..]
> +/**
> + * struct share_obj - SRAM buffer shared with
> + * AP and SCP

Please unwrap this line

> + *
> + * @id: IPI id
> + * @len: share buffer length
> + * @share_buf: share buffer data
> + */
> +struct share_obj {

Please make this struct name slightly less generic, e.g. mtk_share_obj
should be fine.

> + u32 id;
> + u32 len;
> + u8 share_buf[SCP_SHARE_BUFFER_SIZE];
> +};
> +
> +void scp_memcpy_aligned(void __iomem *dst, const void *src, unsigned int len);
> +void scp_ipi_lock(struct mtk_scp *scp, u32 id);
> +void scp_ipi_unlock(struct mtk_scp *scp, u32 id);
> +
> +#endif
> diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c
[..]
> +struct platform_device *scp_get_pdev(struct platform_device *pdev)

I'm unable to find a patch that calls this, but I assume you're only
using the returned struct platform_device * in order to call the other
exported functions in this driver.

If this is the case I would suggest that you return a struct mtk_scp *
instead, as this makes your API cleaner and prevents confusion about
what platform_device could/should be passed in.

Note that you don't need to disclose the struct mtk_scp to your clients,
just add a "struct mtk_scp;" in include/remoteproc/mtk_scp.h and your
clients can pass this pointer around.

> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *scp_node;
> + struct platform_device *scp_pdev;
> +
> + scp_node = of_parse_phandle(dev->of_node, "mediatek,scp", 0);
> + if (!scp_node) {
> + dev_err(dev, "can't get SCP node\n");
> + return NULL;
> + }
> +
> + scp_pdev = of_find_device_by_node(scp_node);
> + if (WARN_ON(!scp_pdev)) {
> + dev_err(dev, "SCP pdev failed\n");
> + of_node_put(scp_node);
> + return NULL;
> + }
> +
> + return scp_pdev;
> +}
> +EXPORT_SYMBOL_GPL(scp_get_pdev);
[..]
> +static irqreturn_t scp_irq_handler(int irq, void *priv)
> +{
> + struct mtk_scp *scp = priv;
> + u32 scp_to_host;
> + int ret;
> +
> + ret = clk_prepare_enable(scp->clk);
> + if (ret) {
> + dev_err(scp->dev, "failed to enable clocks\n");
> + return IRQ_NONE;
> + }
> +
> + scp_to_host = readl(scp->reg_base + MT8183_SCP_TO_HOST);
> + if (scp_to_host & MT8183_SCP_IPC_INT_BIT)
> + scp_ipi_handler(scp);
> + else
> + scp_wdt_handler(scp, scp_to_host);
> +
> + /*
> + * Ensure that all writes to SRAM are committed before another
> + * interrupt.
> + */
> + mb();

writel() should ensure the ordering, is this not sufficient?

> + /* SCP won't send another interrupt until we set SCP_TO_HOST to 0. */
> + writel(MT8183_SCP_IPC_INT_BIT | MT8183_SCP_WDT_INT_BIT,
> + scp->reg_base + MT8183_SCP_TO_HOST);
> + clk_disable_unprepare(scp->clk);
> +
> + return IRQ_HANDLED;
> +}
[..]
> +static int scp_map_memory_region(struct mtk_scp *scp)
> +{
> + int ret;
> +
> + ret = of_reserved_mem_device_init_by_idx(scp->dev, scp->dev->of_node,
> + 0);

As you're passing 0, just use of_reserved_mem_device_init().

> + if (ret) {
> + dev_err(scp->dev,
> + "%s:of_reserved_mem_device_init_by_idx(0) failed:(%d)",
> + __func__, ret);

Please don't use __func__ in your error messages, make this "failed to
assign memory-region: %d\n");

> + return -ENOMEM;
> + }
> +
> + /* Reserved SCP code size */
> + scp->dram_size = MAX_CODE_SIZE;
> + scp->cpu_addr = dma_alloc_coherent(scp->dev, scp->dram_size,
> + &scp->phys_addr, GFP_KERNEL);

Don't you have a problem with that the reserved memory region must be
8MB for this allocation to succeed? If so, consider using devm_ioremap
or similar to map the region.

> + if (!scp->cpu_addr)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static void scp_unmap_memory_region(struct mtk_scp *scp)
> +{
> + dma_free_coherent(scp->dev, scp->dram_size, scp->cpu_addr,
> + scp->phys_addr);
> + of_reserved_mem_device_release(scp->dev);
> +}
> +
> +static int scp_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + struct mtk_scp *scp;
> + struct rproc *rproc;
> + struct resource *res;
> + char *fw_name = "scp.img";
> + int ret, i;
> +
> + rproc = rproc_alloc(dev,
> + np->name,
> + &scp_ops,
> + fw_name,
> + sizeof(*scp));
> + if (!rproc) {
> + dev_err(dev, "unable to allocate remoteproc\n");
> + return -ENOMEM;
> + }
> +
> + scp = (struct mtk_scp *)rproc->priv;
> + scp->rproc = rproc;
> + scp->dev = dev;
> + platform_set_drvdata(pdev, scp);
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
> + scp->sram_base = devm_ioremap_resource(dev, res);
> + if (IS_ERR((__force void *)scp->sram_base)) {
> + dev_err(dev, "Failed to parse and map sram memory\n");
> + ret = PTR_ERR((__force void *)scp->sram_base);
> + goto free_rproc;
> + }
> + scp->sram_size = resource_size(res);
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
> + scp->reg_base = devm_ioremap_resource(dev, res);
> + if (IS_ERR((__force void *)scp->reg_base)) {
> + dev_err(dev, "Failed to parse and map cfg memory\n");
> + ret = PTR_ERR((__force void *)scp->reg_base);
> + goto free_rproc;
> + }
> +
> + ret = scp_map_memory_region(scp);
> + if (ret)
> + goto free_rproc;
> +
> + scp->clk = devm_clk_get(dev, "main");
> + if (IS_ERR(scp->clk)) {
> + dev_err(dev, "Failed to get clock\n");
> + ret = PTR_ERR(scp->clk);
> + goto release_dev_mem;
> + }
> +
> + ret = clk_prepare_enable(scp->clk);
> + if (ret) {
> + dev_err(dev, "failed to enable clocks\n");
> + goto release_dev_mem;
> + }
> +
> + mutex_init(&scp->send_lock);
> + for (i = 0; i < SCP_IPI_MAX; i++)
> + mutex_init(&scp->ipi_desc[i].lock);

Move this chunk up above the platform_get_resource_byname(), so that
it's clear that clk_prepare_enable/clk_disable_unprepare() wraps the
scp_ipi_init().

Also double check that you're hitting destroy_mutex: when necessary.

> +
> + ret = scp_ipi_init(scp);
> + clk_disable_unprepare(scp->clk);
> + if (ret) {
> + dev_err(dev, "Failed to init ipi\n");
> + goto release_dev_mem;
> + }
> +
> + /* register SCP initialization IPI */
> + ret = scp_ipi_register(pdev,
> + SCP_IPI_INIT,
> + scp_init_ipi_handler,
> + scp);
> + if (ret) {
> + dev_err(dev, "Failed to register IPI_SCP_INIT\n");
> + goto release_dev_mem;
> + }
> +
> + init_waitqueue_head(&scp->run.wq);
> + init_waitqueue_head(&scp->ack_wq);
> +
> + ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), NULL,
> + scp_irq_handler, IRQF_ONESHOT,
> + pdev->name, scp);
> +
> + if (ret) {
> + dev_err(dev, "failed to request irq\n");
> + goto destroy_mutex;
> + }
> +
> + ret = rproc_add(rproc);
> + if (ret)
> + goto destroy_mutex;
> +
> + return ret;
> +
> +destroy_mutex:
> + for (i = 0; i < SCP_IPI_MAX; i++)
> + mutex_destroy(&scp->ipi_desc[i].lock);
> + mutex_destroy(&scp->send_lock);
> +release_dev_mem:
> + scp_unmap_memory_region(scp);
> +free_rproc:
> + rproc_free(rproc);
> +
> + return ret;
> +}
> +
> +static int scp_remove(struct platform_device *pdev)
> +{
> + struct mtk_scp *scp = platform_get_drvdata(pdev);
> + int i;
> +
> + for (i = 0; i < SCP_IPI_MAX; i++)
> + mutex_destroy(&scp->ipi_desc[i].lock);
> + mutex_destroy(&scp->send_lock);

rproc_del() serves as a synchronization point for when callbacks
shouldn't be called anymore, so destroy your mutexes after that.

> + rproc_del(scp->rproc);
> + rproc_free(scp->rproc);
> + scp_unmap_memory_region(scp);
> +
> + return 0;
> +}
> +
[..]
> diff --git a/drivers/remoteproc/mtk_scp_ipi.c b/drivers/remoteproc/mtk_scp_ipi.c
[..]
> +/*
> + * Copy src to dst, where dst is in SCP SRAM region.

Please format this as kerneldoc.

> + * Since AP access of SCP SRAM don't support byte write, this always write a
> + * full word at a time, and may cause some extra bytes to be written at the
> + * beginning & ending of dst.
> + */
> +void scp_memcpy_aligned(void __iomem *dst, const void *src, unsigned int len)
> +{
> + void __iomem *ptr;
> + u32 val;
> + unsigned int i = 0;
> +
> + if (!IS_ALIGNED((unsigned long)dst, 4)) {
> + ptr = (void __iomem *)ALIGN_DOWN((unsigned long)dst, 4);
> + i = 4 - (dst - ptr);
> + val = readl_relaxed(ptr);
> + memcpy((u8 *)&val + (4 - i), src, i);
> + writel_relaxed(val, ptr);
> + }
> +
> + while (i + 4 <= len) {
> + val = *((u32 *)(src + i));
> + writel_relaxed(val, dst + i);
> + i += 4;
> + }

Afaict above reimplements __iowrite32_copy().

> + if (i < len) {
> + val = readl_relaxed(dst + i);
> + memcpy(&val, src + i, len - i);
> + writel_relaxed(val, dst + i);
> + }
> +}
> +EXPORT_SYMBOL_GPL(scp_memcpy_aligned);
> +
[..]
> +int scp_ipi_send(struct platform_device *pdev,
> + u32 id,
> + void *buf,
> + unsigned int len,
> + unsigned int wait)
> +{
[..]
> + scp->ipi_id_ack[id] = false;
> + /*
> + * Ensure that all writes to SRAM are committed before sending the
> + * interrupt to SCP.
> + */
> + mb();

Again, isn't the implicit barrier in writel enough?

> + /* send the command to SCP */
> + writel(MT8183_HOST_IPC_INT_BIT, scp->reg_base + MT8183_HOST_TO_SCP);
[..]
> diff --git a/include/linux/remoteproc/mtk_scp.h b/include/linux/remoteproc/mtk_scp.h
[..]
> +/**
> + * scp_ipi_register - register an ipi function

Parenthesis on this, i.e. "scp_ipi_register() - register an ipi function"

> + *
> + * @pdev: SCP platform device
> + * @id: IPI ID
> + * @handler: IPI handler
> + * @priv: private data for IPI handler
> + *
> + * Register an ipi function to receive ipi interrupt from SCP.
> + *
> + * Return: Return 0 if ipi registers successfully, otherwise it is failed.
> + */

Please move the kerneldoc to the implementation instead.

Regards,
Bjorn