From 5ab64902b5f9f800fe677759b1b6ed1f5f749f62 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 27 Aug 2018 20:52:17 -0500 Subject: dmaengine: Convert to using %pOFn instead of device_node.name In preparation to remove the node name pointer from struct device_node, convert printf users to use the %pOFn format specifier. Signed-off-by: Rob Herring Signed-off-by: Vinod Koul --- drivers/dma/nbpfaxi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c index 8c7b2e8703da..8d4914cd06f0 100644 --- a/drivers/dma/nbpfaxi.c +++ b/drivers/dma/nbpfaxi.c @@ -1095,8 +1095,8 @@ static struct dma_chan *nbpf_of_xlate(struct of_phandle_args *dma_spec, if (!dchan) return NULL; - dev_dbg(dchan->device->dev, "Entry %s(%s)\n", __func__, - dma_spec->np->name); + dev_dbg(dchan->device->dev, "Entry %s(%pOFn)\n", __func__, + dma_spec->np); chan = nbpf_to_chan(dchan); -- cgit v1.2.3 From a5c1d8ec733cc354e810bbc30dc0904ddd684976 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 6 Aug 2018 17:43:28 +0800 Subject: dmaengine: st_fdma: use dmaenginem_async_device_register to simplify the code Use dmaenginem_async_device_register to simplify the code: remove dma_async_device_unregister. remove label err_dma_dev Signed-off-by: Huang Shijie Signed-off-by: Vinod Koul --- drivers/dma/st_fdma.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c index bfb79bd0c6de..07c20aa2e955 100644 --- a/drivers/dma/st_fdma.c +++ b/drivers/dma/st_fdma.c @@ -833,7 +833,7 @@ static int st_fdma_probe(struct platform_device *pdev) fdev->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); fdev->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; - ret = dma_async_device_register(&fdev->dma_device); + ret = dmaenginem_async_device_register(&fdev->dma_device); if (ret) { dev_err(&pdev->dev, "Failed to register DMA device (%d)\n", ret); @@ -844,15 +844,13 @@ static int st_fdma_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Failed to register controller (%d)\n", ret); - goto err_dma_dev; + goto err_rproc; } dev_info(&pdev->dev, "ST FDMA engine driver, irq:%d\n", fdev->irq); return 0; -err_dma_dev: - dma_async_device_unregister(&fdev->dma_device); err_rproc: st_fdma_free(fdev); st_slim_rproc_put(fdev->slim_rproc); @@ -867,7 +865,6 @@ static int st_fdma_remove(struct platform_device *pdev) devm_free_irq(&pdev->dev, fdev->irq, fdev); st_slim_rproc_put(fdev->slim_rproc); of_dma_controller_free(pdev->dev.of_node); - dma_async_device_unregister(&fdev->dma_device); return 0; } -- cgit v1.2.3 From c88c2d46e29bf18f882b5afec231a4f86b578269 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 6 Aug 2018 16:52:23 +0800 Subject: dmaengine: dw-axi-dmac: use dmaenginem_async_device_register to simplify the code Use dmaenginem_async_device_register to simplify the code: remove dma_async_device_unregister. Signed-off-by: Huang Shijie Signed-off-by: Vinod Koul --- drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index c4eb55e3011c..b2ac1d2c5b86 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -934,7 +934,7 @@ static int dw_probe(struct platform_device *pdev) pm_runtime_put(chip->dev); - ret = dma_async_device_register(&dw->dma); + ret = dmaenginem_async_device_register(&dw->dma); if (ret) goto err_pm_disable; @@ -977,8 +977,6 @@ static int dw_remove(struct platform_device *pdev) tasklet_kill(&chan->vc.task); } - dma_async_device_unregister(&dw->dma); - return 0; } -- cgit v1.2.3 From 42f604b6516b9e4d6847f6461aa4260effe68f55 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 6 Aug 2018 16:52:24 +0800 Subject: dmaengine: stm32-mdma: use dmaenginem_async_device_register to simplify the code Use dmaenginem_async_device_register to simplify the code: remove dma_async_device_unregister. Acked-by: Pierre-Yves MORDRET Signed-off-by: Huang Shijie Signed-off-by: Vinod Koul --- drivers/dma/stm32-mdma.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/stm32-mdma.c b/drivers/dma/stm32-mdma.c index 06dd1725375e..390e4cae0e1a 100644 --- a/drivers/dma/stm32-mdma.c +++ b/drivers/dma/stm32-mdma.c @@ -1656,7 +1656,7 @@ static int stm32_mdma_probe(struct platform_device *pdev) return ret; } - ret = dma_async_device_register(dd); + ret = dmaenginem_async_device_register(dd); if (ret) return ret; @@ -1674,8 +1674,6 @@ static int stm32_mdma_probe(struct platform_device *pdev) return 0; err_unregister: - dma_async_device_unregister(dd); - return ret; } -- cgit v1.2.3 From a5f99a95869e3052970ee17a2d9911f09ad818cd Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 6 Aug 2018 16:52:26 +0800 Subject: dmaengine: mmp_tdma: use dmaenginem_async_device_register to simplify the code Use dmaenginem_async_device_register to simplify the code: remove dma_async_device_unregister. return error if it fails. Signed-off-by: Huang Shijie Signed-off-by: Vinod Koul --- drivers/dma/mmp_tdma.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index 13c68b6434ce..9f5869e1a017 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -530,9 +530,6 @@ static void mmp_tdma_issue_pending(struct dma_chan *chan) static int mmp_tdma_remove(struct platform_device *pdev) { - struct mmp_tdma_device *tdev = platform_get_drvdata(pdev); - - dma_async_device_unregister(&tdev->device); return 0; } @@ -696,7 +693,7 @@ static int mmp_tdma_probe(struct platform_device *pdev) dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); platform_set_drvdata(pdev, tdev); - ret = dma_async_device_register(&tdev->device); + ret = dmaenginem_async_device_register(&tdev->device); if (ret) { dev_err(tdev->device.dev, "unable to register\n"); return ret; @@ -708,7 +705,7 @@ static int mmp_tdma_probe(struct platform_device *pdev) if (ret) { dev_err(tdev->device.dev, "failed to register controller\n"); - dma_async_device_unregister(&tdev->device); + return ret; } } -- cgit v1.2.3 From fc9826dec54c445d1bfbb8fd85d3382dc90bb110 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 6 Aug 2018 16:52:27 +0800 Subject: dmaengine: ste_dma40: use dmaenginem_async_device_register to simplify the code Use dmaenginem_async_device_register to simplify the code: remove dma_async_device_unregister. remove label unregister_slave, unregister_memcpy Signed-off-by: Huang Shijie Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index f4edfc56f34e..5e328bd10c27 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2839,7 +2839,7 @@ static int __init d40_dmaengine_init(struct d40_base *base, d40_ops_init(base, &base->dma_slave); - err = dma_async_device_register(&base->dma_slave); + err = dmaenginem_async_device_register(&base->dma_slave); if (err) { d40_err(base->dev, "Failed to register slave channels\n"); @@ -2854,12 +2854,12 @@ static int __init d40_dmaengine_init(struct d40_base *base, d40_ops_init(base, &base->dma_memcpy); - err = dma_async_device_register(&base->dma_memcpy); + err = dmaenginem_async_device_register(&base->dma_memcpy); if (err) { d40_err(base->dev, "Failed to register memcpy only channels\n"); - goto unregister_slave; + goto exit; } d40_chan_init(base, &base->dma_both, base->phy_chans, @@ -2871,18 +2871,14 @@ static int __init d40_dmaengine_init(struct d40_base *base, dma_cap_set(DMA_CYCLIC, base->dma_slave.cap_mask); d40_ops_init(base, &base->dma_both); - err = dma_async_device_register(&base->dma_both); + err = dmaenginem_async_device_register(&base->dma_both); if (err) { d40_err(base->dev, "Failed to register logical and physical capable channels\n"); - goto unregister_memcpy; + goto exit; } return 0; - unregister_memcpy: - dma_async_device_unregister(&base->dma_memcpy); - unregister_slave: - dma_async_device_unregister(&base->dma_slave); exit: return err; } -- cgit v1.2.3 From 0f5a5e5700adeef1dc9660ec862f52506096c711 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 6 Aug 2018 16:52:28 +0800 Subject: dmaengine: dma-jz4780: use dmaenginem_async_device_register to simplify the code Use dmaenginem_async_device_register to simplify the code: remove dma_async_device_unregister. remove label err_unregister_dev Signed-off-by: Huang Shijie Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index 85820a2d69d4..b2a244902bc5 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -847,7 +847,7 @@ static int jz4780_dma_probe(struct platform_device *pdev) jzchan->vchan.desc_free = jz4780_dma_desc_free; } - ret = dma_async_device_register(dd); + ret = dmaenginem_async_device_register(dd); if (ret) { dev_err(dev, "failed to register device\n"); goto err_disable_clk; @@ -858,15 +858,12 @@ static int jz4780_dma_probe(struct platform_device *pdev) jzdma); if (ret) { dev_err(dev, "failed to register OF DMA controller\n"); - goto err_unregister_dev; + goto err_disable_clk; } dev_info(dev, "JZ4780 DMA controller initialised\n"); return 0; -err_unregister_dev: - dma_async_device_unregister(dd); - err_disable_clk: clk_disable_unprepare(jzdma->clk); @@ -887,7 +884,6 @@ static int jz4780_dma_remove(struct platform_device *pdev) for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) tasklet_kill(&jzdma->chan[i].vchan.task); - dma_async_device_unregister(&jzdma->dma_device); return 0; } -- cgit v1.2.3 From d72c5f98539d092692d5d94dfcb74e1d27588907 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 6 Aug 2018 16:52:29 +0800 Subject: dmaengine: pxa_dma: use dmaenginem_async_device_register to simplify the code Use dmaenginem_async_device_register to simplify the code: remove dma_async_device_unregister. Signed-off-by: Huang Shijie Acked-by: Robert Jarzmik Signed-off-by: Vinod Koul --- drivers/dma/pxa_dma.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c index b31c28b67ad3..21001ea078fb 100644 --- a/drivers/dma/pxa_dma.c +++ b/drivers/dma/pxa_dma.c @@ -1285,7 +1285,6 @@ static int pxad_remove(struct platform_device *op) pxad_cleanup_debugfs(pdev); pxad_free_channels(&pdev->slave); - dma_async_device_unregister(&pdev->slave); return 0; } @@ -1396,7 +1395,7 @@ static int pxad_init_dmadev(struct platform_device *op, init_waitqueue_head(&c->wq_state); } - return dma_async_device_register(&pdev->slave); + return dmaenginem_async_device_register(&pdev->slave); } static int pxad_probe(struct platform_device *op) -- cgit v1.2.3 From fbb69ece0462ae95bbdff3ade9288bb5f1709f26 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 6 Aug 2018 16:52:30 +0800 Subject: dmaengine: mxs-dma: use dmaenginem_async_device_register to simplify the code Use dmaenginem_async_device_register to simplify the code: remove dma_async_device_unregister. Signed-off-by: Huang Shijie Signed-off-by: Vinod Koul --- drivers/dma/mxs-dma.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index ae5182ff0128..35193b31a9e0 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -847,7 +847,7 @@ static int __init mxs_dma_probe(struct platform_device *pdev) mxs_dma->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; mxs_dma->dma_device.device_issue_pending = mxs_dma_enable_chan; - ret = dma_async_device_register(&mxs_dma->dma_device); + ret = dmaenginem_async_device_register(&mxs_dma->dma_device); if (ret) { dev_err(mxs_dma->dma_device.dev, "unable to register\n"); return ret; @@ -857,7 +857,6 @@ static int __init mxs_dma_probe(struct platform_device *pdev) if (ret) { dev_err(mxs_dma->dma_device.dev, "failed to register controller\n"); - dma_async_device_unregister(&mxs_dma->dma_device); } dev_info(mxs_dma->dma_device.dev, "initialized\n"); -- cgit v1.2.3 From 90082cd397aebc8ce375904024d7c80d1258df85 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 6 Aug 2018 16:52:31 +0800 Subject: dmaengine: add COMPILE_TEST for the drivers We can do the compiling test with COMPILE_TEST. This patch adds the COMPILE_TEST for the drivers. Signed-off-by: Huang Shijie Acked-by: Linus Walleij Acked-by: Robert Jarzmik Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index dacf3f42426d..73a75dea9cc3 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -447,7 +447,7 @@ config PL330_DMA config PXA_DMA bool "PXA DMA support" - depends on (ARCH_MMP || ARCH_PXA) + depends on (ARCH_MMP || ARCH_PXA || COMPILE_TEST) select DMA_ENGINE select DMA_VIRTUAL_CHANNELS help @@ -465,7 +465,7 @@ config SIRF_DMA config STE_DMA40 bool "ST-Ericsson DMA40 support" - depends on ARCH_U8500 + depends on ARCH_U8500 || COMPILE_TEST select DMA_ENGINE help Support for ST-Ericsson DMA40 controller -- cgit v1.2.3 From 5658f4f94ccab6fa93af82368a63256011f9592c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 27 Aug 2018 09:45:45 +0200 Subject: dmaengine: dw: Add alternative ACPI HIDs for Cherry Trail DMA controllers Bay and Cherry Trail DSTDs represent a different set of devices depending on which OS the device think it is booting. One set of decices for Windows and another set of devices for Android which targets the Android-x86 Linux kernel fork (which e.g. used to have its own display driver instead of using the i915 driver). Which set of devices we are actually going to get is out of our control, this is controlled by the ACPI OSID variable, which gets either set through an EFI setup option, or sometimes is autodetected. So we need to support both. This commit adds support for the 80862286 and 808622C0 ACPI HIDs which we get for the first resp. second DMA controller on Cherry Trail devices when OSID is set to Android. Signed-off-by: Hans de Goede Signed-off-by: Vinod Koul --- drivers/dma/dw/platform.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/dma') diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index f62dd0944908..f01b2c173fa6 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -284,6 +284,8 @@ MODULE_DEVICE_TABLE(of, dw_dma_of_id_table); #ifdef CONFIG_ACPI static const struct acpi_device_id dw_dma_acpi_id_table[] = { { "INTL9C60", 0 }, + { "80862286", 0 }, + { "808622C0", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table); -- cgit v1.2.3 From 901fd85251e5dea328d287352d99e342d05c6e09 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 23 Aug 2018 15:34:54 +0200 Subject: dmaengine: use SPDX identifier for Renesas drivers Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Vinod Koul --- drivers/dma/nbpfaxi.c | 5 +---- drivers/dma/sh/shdma-arm.h | 5 +---- drivers/dma/sh/shdma-base.c | 5 +---- drivers/dma/sh/shdma-of.c | 5 +---- drivers/dma/sh/shdma-r8a73a4.c | 5 +---- drivers/dma/sh/shdma.h | 6 +----- drivers/dma/sh/shdmac.c | 6 +----- drivers/dma/sh/sudmac.c | 5 +---- drivers/dma/sh/usb-dmac.c | 5 +---- 9 files changed, 9 insertions(+), 38 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c index 8c7b2e8703da..38d251b198b8 100644 --- a/drivers/dma/nbpfaxi.c +++ b/drivers/dma/nbpfaxi.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2013-2014 Renesas Electronics Europe Ltd. * Author: Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. */ #include diff --git a/drivers/dma/sh/shdma-arm.h b/drivers/dma/sh/shdma-arm.h index a1b0ef45d6a2..7459f9a13b5b 100644 --- a/drivers/dma/sh/shdma-arm.h +++ b/drivers/dma/sh/shdma-arm.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Renesas SuperH DMA Engine support * * Copyright (C) 2013 Renesas Electronics, Inc. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of version 2 the GNU General Public License as published by the Free - * Software Foundation. */ #ifndef SHDMA_ARM_H diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 6b5626e299b2..c51de498b5b4 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Dmaengine driver base library for DMA controllers, found on SH-based SoCs * @@ -7,10 +8,6 @@ * Copyright (C) 2009 Nobuhiro Iwamatsu * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved. * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. */ #include diff --git a/drivers/dma/sh/shdma-of.c b/drivers/dma/sh/shdma-of.c index f999f9b0d314..be89dd894328 100644 --- a/drivers/dma/sh/shdma-of.c +++ b/drivers/dma/sh/shdma-of.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * SHDMA Device Tree glue * * Copyright (C) 2013 Renesas Electronics Inc. * Author: Guennadi Liakhovetski - * - * This is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. */ #include diff --git a/drivers/dma/sh/shdma-r8a73a4.c b/drivers/dma/sh/shdma-r8a73a4.c index 96ea3828c3eb..ddc9a3578353 100644 --- a/drivers/dma/sh/shdma-r8a73a4.c +++ b/drivers/dma/sh/shdma-r8a73a4.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas SuperH DMA Engine support for r8a73a4 (APE6) SoCs * * Copyright (C) 2013 Renesas Electronics, Inc. - * - * This is free software; you can redistribute it and/or modify it under the - * terms of version 2 the GNU General Public License as published by the Free - * Software Foundation. */ #include diff --git a/drivers/dma/sh/shdma.h b/drivers/dma/sh/shdma.h index 2c0a969adc9f..bfb69909bd19 100644 --- a/drivers/dma/sh/shdma.h +++ b/drivers/dma/sh/shdma.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Renesas SuperH DMA Engine support * * Copyright (C) 2009 Nobuhiro Iwamatsu * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved. * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * */ #ifndef __DMA_SHDMA_H #define __DMA_SHDMA_H diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index 04a74e0a95b7..7971ea275387 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Renesas SuperH DMA Engine support * @@ -8,11 +9,6 @@ * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved. * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * * - DMA of SuperH does not have Hardware DMA chain mode. * - MAX DMA size is 16MB. * diff --git a/drivers/dma/sh/sudmac.c b/drivers/dma/sh/sudmac.c index 69b9564dc9d9..30cc3553cb8b 100644 --- a/drivers/dma/sh/sudmac.c +++ b/drivers/dma/sh/sudmac.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas SUDMAC support * @@ -8,10 +9,6 @@ * Copyright (C) 2009 Nobuhiro Iwamatsu * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved. * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. */ #include diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c index 1bb1a8e09025..7f7184c3cf95 100644 --- a/drivers/dma/sh/usb-dmac.c +++ b/drivers/dma/sh/usb-dmac.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas USB DMA Controller Driver * @@ -6,10 +7,6 @@ * based on rcar-dmac.c * Copyright (C) 2014 Renesas Electronics Inc. * Author: Laurent Pinchart - * - * This is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. */ #include -- cgit v1.2.3 From 1297b647c58c5d1d576654acf93c3dabb615256a Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 17 Aug 2018 06:00:26 -0700 Subject: dmaengine: fsldma: move spin_lock_bh to spin_lock in tasklet as you are already in a tasklet, it is unnecessary to call spin_lock_bh. Signed-off-by: Barry Song <21cnbao@gmail.com> Signed-off-by: Vinod Koul --- drivers/dma/fsldma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 1117b5123a6f..9d360a3fbae3 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -987,7 +987,7 @@ static void dma_do_tasklet(unsigned long data) chan_dbg(chan, "tasklet entry\n"); - spin_lock_bh(&chan->desc_lock); + spin_lock(&chan->desc_lock); /* the hardware is now idle and ready for more */ chan->idle = true; @@ -995,7 +995,7 @@ static void dma_do_tasklet(unsigned long data) /* Run all cleanup for descriptors which have been completed */ fsldma_cleanup_descriptors(chan); - spin_unlock_bh(&chan->desc_lock); + spin_unlock(&chan->desc_lock); chan_dbg(chan, "tasklet exit\n"); } -- cgit v1.2.3 From cbc229a47c82052677cac3066db7cb90aa8ffdc2 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 17 Aug 2018 06:02:02 -0700 Subject: dmaengine: mv_xor: move spin_lock_bh to spin_lock in tasklet as you are already in a tasklet, it is unnecessary to call spin_lock_bh. Signed-off-by: Barry Song <21cnbao@gmail.com> Signed-off-by: Vinod Koul --- drivers/dma/mv_xor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 969534c1a6c6..7f595355fb79 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -348,9 +348,9 @@ static void mv_xor_tasklet(unsigned long data) { struct mv_xor_chan *chan = (struct mv_xor_chan *) data; - spin_lock_bh(&chan->lock); + spin_lock(&chan->lock); mv_chan_slot_cleanup(chan); - spin_unlock_bh(&chan->lock); + spin_unlock(&chan->lock); } static struct mv_xor_desc_slot * -- cgit v1.2.3 From d8570d018f69de60ec65790083767fd2e44a6c7a Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 17 Aug 2018 06:03:43 -0700 Subject: dmaengine: at_xdmac: move spin_lock_bh to spin_lock in tasklet as you are already in a tasklet, it is unnecessary to call spin_lock_bh. Signed-off-by: Barry Song <21cnbao@gmail.com> Acked-by: Ludovic Desroches Signed-off-by: Vinod Koul --- drivers/dma/at_xdmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index 4bf72561667c..4e557684f792 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -1600,7 +1600,7 @@ static void at_xdmac_tasklet(unsigned long data) if (atchan->status & AT_XDMAC_CIS_ROIS) dev_err(chan2dev(&atchan->chan), "request overflow error!!!"); - spin_lock_bh(&atchan->lock); + spin_lock(&atchan->lock); desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc, xfer_node); @@ -1610,7 +1610,7 @@ static void at_xdmac_tasklet(unsigned long data) txd = &desc->tx_dma_desc; at_xdmac_remove_xfer(atchan, desc); - spin_unlock_bh(&atchan->lock); + spin_unlock(&atchan->lock); if (!at_xdmac_chan_is_cyclic(atchan)) { dma_cookie_complete(txd); -- cgit v1.2.3 From 4ac695464763ecf696eaba563eff1c2ab994f6d8 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Tue, 28 Aug 2018 19:09:07 +0800 Subject: dmaengine: sprd: Support DMA link-list mode The Spreadtrum DMA can support the link-list transaction mode, which means DMA controller can do transaction one by one automatically once we linked these transaction by link-list register. Signed-off-by: Eric Long Signed-off-by: Baolin Wang Signed-off-by: Vinod Koul --- drivers/dma/sprd-dma.c | 81 ++++++++++++++++++++++++++++++++++++++++---- include/linux/dma/sprd-dma.h | 69 +++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 7 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index 55df0d41355b..38d4e4f07c66 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c @@ -68,6 +68,7 @@ /* SPRD_DMA_CHN_CFG register definition */ #define SPRD_DMA_CHN_EN BIT(0) +#define SPRD_DMA_LINKLIST_EN BIT(4) #define SPRD_DMA_WAIT_BDONE_OFFSET 24 #define SPRD_DMA_DONOT_WAIT_BDONE 1 @@ -103,7 +104,7 @@ #define SPRD_DMA_REQ_MODE_MASK GENMASK(1, 0) #define SPRD_DMA_FIX_SEL_OFFSET 21 #define SPRD_DMA_FIX_EN_OFFSET 20 -#define SPRD_DMA_LLIST_END_OFFSET 19 +#define SPRD_DMA_LLIST_END BIT(19) #define SPRD_DMA_FRG_LEN_MASK GENMASK(16, 0) /* SPRD_DMA_CHN_BLK_LEN register definition */ @@ -164,6 +165,7 @@ struct sprd_dma_desc { struct sprd_dma_chn { struct virt_dma_chan vc; void __iomem *chn_base; + struct sprd_dma_linklist linklist; struct dma_slave_config slave_cfg; u32 chn_num; u32 dev_id; @@ -582,7 +584,8 @@ static int sprd_dma_get_step(enum dma_slave_buswidth buswidth) } static int sprd_dma_fill_desc(struct dma_chan *chan, - struct sprd_dma_desc *sdesc, + struct sprd_dma_chn_hw *hw, + unsigned int sglen, int sg_index, dma_addr_t src, dma_addr_t dst, u32 len, enum dma_transfer_direction dir, unsigned long flags, @@ -590,7 +593,6 @@ static int sprd_dma_fill_desc(struct dma_chan *chan, { struct sprd_dma_dev *sdev = to_sprd_dma_dev(chan); struct sprd_dma_chn *schan = to_sprd_dma_chan(chan); - struct sprd_dma_chn_hw *hw = &sdesc->chn_hw; u32 req_mode = (flags >> SPRD_DMA_REQ_SHIFT) & SPRD_DMA_REQ_MODE_MASK; u32 int_mode = flags & SPRD_DMA_INT_MASK; int src_datawidth, dst_datawidth, src_step, dst_step; @@ -670,12 +672,52 @@ static int sprd_dma_fill_desc(struct dma_chan *chan, temp |= (src_step & SPRD_DMA_TRSF_STEP_MASK) << SPRD_DMA_SRC_TRSF_STEP_OFFSET; hw->trsf_step = temp; + /* link-list configuration */ + if (schan->linklist.phy_addr) { + if (sg_index == sglen - 1) + hw->frg_len |= SPRD_DMA_LLIST_END; + + hw->cfg |= SPRD_DMA_LINKLIST_EN; + + /* link-list index */ + temp = (sg_index + 1) % sglen; + /* Next link-list configuration's physical address offset */ + temp = temp * sizeof(*hw) + SPRD_DMA_CHN_SRC_ADDR; + /* + * Set the link-list pointer point to next link-list + * configuration's physical address. + */ + hw->llist_ptr = schan->linklist.phy_addr + temp; + } else { + hw->llist_ptr = 0; + } + hw->frg_step = 0; hw->src_blk_step = 0; hw->des_blk_step = 0; return 0; } +static int sprd_dma_fill_linklist_desc(struct dma_chan *chan, + unsigned int sglen, int sg_index, + dma_addr_t src, dma_addr_t dst, u32 len, + enum dma_transfer_direction dir, + unsigned long flags, + struct dma_slave_config *slave_cfg) +{ + struct sprd_dma_chn *schan = to_sprd_dma_chan(chan); + struct sprd_dma_chn_hw *hw; + + if (!schan->linklist.virt_addr) + return -EINVAL; + + hw = (struct sprd_dma_chn_hw *)(schan->linklist.virt_addr + + sg_index * sizeof(*hw)); + + return sprd_dma_fill_desc(chan, hw, sglen, sg_index, src, dst, len, + dir, flags, slave_cfg); +} + static struct dma_async_tx_descriptor * sprd_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, size_t len, unsigned long flags) @@ -744,10 +786,20 @@ sprd_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, u32 len = 0; int ret, i; - /* TODO: now we only support one sg for each DMA configuration. */ - if (!is_slave_direction(dir) || sglen > 1) + if (!is_slave_direction(dir)) return NULL; + if (context) { + struct sprd_dma_linklist *ll_cfg = + (struct sprd_dma_linklist *)context; + + schan->linklist.phy_addr = ll_cfg->phy_addr; + schan->linklist.virt_addr = ll_cfg->virt_addr; + } else { + schan->linklist.phy_addr = 0; + schan->linklist.virt_addr = 0; + } + sdesc = kzalloc(sizeof(*sdesc), GFP_NOWAIT); if (!sdesc) return NULL; @@ -762,10 +814,25 @@ sprd_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, src = slave_cfg->src_addr; dst = sg_dma_address(sg); } + + /* + * The link-list mode needs at least 2 link-list + * configurations. If there is only one sg, it doesn't + * need to fill the link-list configuration. + */ + if (sglen < 2) + break; + + ret = sprd_dma_fill_linklist_desc(chan, sglen, i, src, dst, len, + dir, flags, slave_cfg); + if (ret) { + kfree(sdesc); + return NULL; + } } - ret = sprd_dma_fill_desc(chan, sdesc, src, dst, len, dir, flags, - slave_cfg); + ret = sprd_dma_fill_desc(chan, &sdesc->chn_hw, 0, 0, src, dst, len, + dir, flags, slave_cfg); if (ret) { kfree(sdesc); return NULL; diff --git a/include/linux/dma/sprd-dma.h b/include/linux/dma/sprd-dma.h index b0115e340fbc..b42b80e52cc2 100644 --- a/include/linux/dma/sprd-dma.h +++ b/include/linux/dma/sprd-dma.h @@ -58,4 +58,73 @@ enum sprd_dma_int_type { SPRD_DMA_CFGERR_INT, }; +/* + * struct sprd_dma_linklist - DMA link-list address structure + * @virt_addr: link-list virtual address to configure link-list node + * @phy_addr: link-list physical address to link DMA transfer + * + * The Spreadtrum DMA controller supports the link-list mode, that means slaves + * can supply several groups configurations (each configuration represents one + * DMA transfer) saved in memory, and DMA controller will link these groups + * configurations by writing the physical address of each configuration into the + * link-list register. + * + * Just as shown below, the link-list pointer register will be pointed to the + * physical address of 'configuration 1', and the 'configuration 1' link-list + * pointer will be pointed to 'configuration 2', and so on. + * Once trigger the DMA transfer, the DMA controller will load 'configuration + * 1' to its registers automatically, after 'configuration 1' transaction is + * done, DMA controller will load 'configuration 2' automatically, until all + * DMA transactions are done. + * + * Note: The last link-list pointer should point to the physical address + * of 'configuration 1', which can avoid DMA controller loads incorrect + * configuration when the last configuration transaction is done. + * + * DMA controller linklist memory + * ====================== ----------------------- + *| | | configuration 1 |<--- + *| DMA controller | ------->| | | + *| | | | | | + *| | | | | | + *| | | | | | + *| linklist pointer reg |---- ----| linklist pointer | | + * ====================== | ----------------------- | + * | | + * | ----------------------- | + * | | configuration 2 | | + * --->| | | + * | | | + * | | | + * | | | + * ----| linklist pointer | | + * | ----------------------- | + * | | + * | ----------------------- | + * | | configuration 3 | | + * --->| | | + * | | | + * | . | | + * . | + * . | + * . | + * | . | + * | ----------------------- | + * | | configuration n | | + * --->| | | + * | | | + * | | | + * | | | + * | linklist pointer |---- + * ----------------------- + * + * To support the link-list mode, DMA slaves should allocate one segment memory + * from always-on IRAM or dma coherent memory to store these groups of DMA + * configuration, and pass the virtual and physical address to DMA controller. + */ +struct sprd_dma_linklist { + unsigned long virt_addr; + phys_addr_t phy_addr; +}; + #endif -- cgit v1.2.3 From 90d23222cf5158d6cc724347e8940b4a87779e30 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 11 Sep 2018 11:36:33 +0530 Subject: dmaengine: Revert "dmaengine: add COMPILE_TEST for the drivers" We have build failures attributed to turning on COMPILE_TEST, so revert commit 90082cd397aeb: ("dmaengine: add COMPILE_TEST for the drivers") while we fix these. Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 73a75dea9cc3..dacf3f42426d 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -447,7 +447,7 @@ config PL330_DMA config PXA_DMA bool "PXA DMA support" - depends on (ARCH_MMP || ARCH_PXA || COMPILE_TEST) + depends on (ARCH_MMP || ARCH_PXA) select DMA_ENGINE select DMA_VIRTUAL_CHANNELS help @@ -465,7 +465,7 @@ config SIRF_DMA config STE_DMA40 bool "ST-Ericsson DMA40 support" - depends on ARCH_U8500 || COMPILE_TEST + depends on ARCH_U8500 select DMA_ENGINE help Support for ST-Ericsson DMA40 controller -- cgit v1.2.3 From a436ff1e96b9e1f34952915fe143a75a4ad8b561 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Wed, 29 Aug 2018 14:04:26 -0700 Subject: dmaengine: pxa: fix semicolon.cocci warnings Remove unneeded semicolon. Generated by: scripts/coccinelle/misc/semicolon.cocci Signed-off-by: kbuild test robot Signed-off-by: Julia Lawall Signed-off-by: Vinod Koul --- drivers/dma/pxa_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c index 21001ea078fb..825725057e00 100644 --- a/drivers/dma/pxa_dma.c +++ b/drivers/dma/pxa_dma.c @@ -1432,7 +1432,7 @@ static int pxad_probe(struct platform_device *op) "#dma-requests set to default 32 as missing in OF: %d", ret); nb_requestors = 32; - }; + } } else if (pdata && pdata->dma_channels) { dma_channels = pdata->dma_channels; nb_requestors = pdata->nb_requestors; -- cgit v1.2.3 From 9d831528a6567da92d1ce2a77c575af29068d063 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Sun, 19 Aug 2018 19:27:13 +0200 Subject: dmaengine: fsl-edma: extract common fsl-edma code (no changes in behavior intended) This patch adds a new fsl-edma-common module to allow new mcf-edma module code to use most of the fsl-edma code. Signed-off-by: Angelo Dureghello Tested-by: Krzysztof Kozlowski Signed-off-by: Vinod Koul --- drivers/dma/Makefile | 2 +- drivers/dma/fsl-edma-common.c | 552 +++++++++++++++++++++++++++++++++ drivers/dma/fsl-edma-common.h | 241 +++++++++++++++ drivers/dma/fsl-edma.c | 697 +----------------------------------------- 4 files changed, 795 insertions(+), 697 deletions(-) create mode 100644 drivers/dma/fsl-edma-common.c create mode 100644 drivers/dma/fsl-edma-common.h (limited to 'drivers/dma') diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index c91702d88b95..21db4e51b246 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -31,7 +31,7 @@ obj-$(CONFIG_DW_AXI_DMAC) += dw-axi-dmac/ obj-$(CONFIG_DW_DMAC_CORE) += dw/ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o obj-$(CONFIG_FSL_DMA) += fsldma.o -obj-$(CONFIG_FSL_EDMA) += fsl-edma.o +obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o obj-$(CONFIG_FSL_RAID) += fsl_raid.o obj-$(CONFIG_HSU_DMA) += hsu/ obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c new file mode 100644 index 000000000000..3fc786b3d03c --- /dev/null +++ b/drivers/dma/fsl-edma-common.c @@ -0,0 +1,552 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (c) 2013-2014 Freescale Semiconductor, Inc +// Copyright (c) 2017 Sysam, Angelo Dureghello + +#include +#include +#include + +#include "fsl-edma-common.h" + +#define EDMA_CR 0x00 +#define EDMA_ES 0x04 +#define EDMA_ERQ 0x0C +#define EDMA_EEI 0x14 +#define EDMA_SERQ 0x1B +#define EDMA_CERQ 0x1A +#define EDMA_SEEI 0x19 +#define EDMA_CEEI 0x18 +#define EDMA_CINT 0x1F +#define EDMA_CERR 0x1E +#define EDMA_SSRT 0x1D +#define EDMA_CDNE 0x1C +#define EDMA_INTR 0x24 +#define EDMA_ERR 0x2C + +#define EDMA64_ERQH 0x08 +#define EDMA64_EEIH 0x10 +#define EDMA64_SERQ 0x18 +#define EDMA64_CERQ 0x19 +#define EDMA64_SEEI 0x1a +#define EDMA64_CEEI 0x1b +#define EDMA64_CINT 0x1c +#define EDMA64_CERR 0x1d +#define EDMA64_SSRT 0x1e +#define EDMA64_CDNE 0x1f +#define EDMA64_INTH 0x20 +#define EDMA64_INTL 0x24 +#define EDMA64_ERRH 0x28 +#define EDMA64_ERRL 0x2c + +#define EDMA_TCD 0x1000 + +static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan) +{ + void __iomem *addr = fsl_chan->edma->membase; + u32 ch = fsl_chan->vchan.chan.chan_id; + + edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), addr + EDMA_SEEI); + edma_writeb(fsl_chan->edma, ch, addr + EDMA_SERQ); +} + +void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan) +{ + void __iomem *addr = fsl_chan->edma->membase; + u32 ch = fsl_chan->vchan.chan.chan_id; + + edma_writeb(fsl_chan->edma, ch, addr + EDMA_CERQ); + edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), addr + EDMA_CEEI); +} +EXPORT_SYMBOL_GPL(fsl_edma_disable_request); + +void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan, + unsigned int slot, bool enable) +{ + u32 ch = fsl_chan->vchan.chan.chan_id; + void __iomem *muxaddr; + unsigned int chans_per_mux, ch_off; + + chans_per_mux = fsl_chan->edma->n_chans / DMAMUX_NR; + ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux; + muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux]; + slot = EDMAMUX_CHCFG_SOURCE(slot); + + if (enable) + iowrite8(EDMAMUX_CHCFG_ENBL | slot, muxaddr + ch_off); + else + iowrite8(EDMAMUX_CHCFG_DIS, muxaddr + ch_off); +} +EXPORT_SYMBOL_GPL(fsl_edma_chan_mux); + +static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width) +{ + switch (addr_width) { + case 1: + return EDMA_TCD_ATTR_SSIZE_8BIT | EDMA_TCD_ATTR_DSIZE_8BIT; + case 2: + return EDMA_TCD_ATTR_SSIZE_16BIT | EDMA_TCD_ATTR_DSIZE_16BIT; + case 4: + return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT; + case 8: + return EDMA_TCD_ATTR_SSIZE_64BIT | EDMA_TCD_ATTR_DSIZE_64BIT; + default: + return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT; + } +} + +void fsl_edma_free_desc(struct virt_dma_desc *vdesc) +{ + struct fsl_edma_desc *fsl_desc; + int i; + + fsl_desc = to_fsl_edma_desc(vdesc); + for (i = 0; i < fsl_desc->n_tcds; i++) + dma_pool_free(fsl_desc->echan->tcd_pool, fsl_desc->tcd[i].vtcd, + fsl_desc->tcd[i].ptcd); + kfree(fsl_desc); +} +EXPORT_SYMBOL_GPL(fsl_edma_free_desc); + +int fsl_edma_terminate_all(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + fsl_edma_disable_request(fsl_chan); + fsl_chan->edesc = NULL; + fsl_chan->idle = true; + vchan_get_all_descriptors(&fsl_chan->vchan, &head); + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + vchan_dma_desc_free_list(&fsl_chan->vchan, &head); + return 0; +} +EXPORT_SYMBOL_GPL(fsl_edma_terminate_all); + +int fsl_edma_pause(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + if (fsl_chan->edesc) { + fsl_edma_disable_request(fsl_chan); + fsl_chan->status = DMA_PAUSED; + fsl_chan->idle = true; + } + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + return 0; +} +EXPORT_SYMBOL_GPL(fsl_edma_pause); + +int fsl_edma_resume(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + if (fsl_chan->edesc) { + fsl_edma_enable_request(fsl_chan); + fsl_chan->status = DMA_IN_PROGRESS; + fsl_chan->idle = false; + } + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + return 0; +} +EXPORT_SYMBOL_GPL(fsl_edma_resume); + +int fsl_edma_slave_config(struct dma_chan *chan, + struct dma_slave_config *cfg) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + + fsl_chan->fsc.dir = cfg->direction; + if (cfg->direction == DMA_DEV_TO_MEM) { + fsl_chan->fsc.dev_addr = cfg->src_addr; + fsl_chan->fsc.addr_width = cfg->src_addr_width; + fsl_chan->fsc.burst = cfg->src_maxburst; + fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width); + } else if (cfg->direction == DMA_MEM_TO_DEV) { + fsl_chan->fsc.dev_addr = cfg->dst_addr; + fsl_chan->fsc.addr_width = cfg->dst_addr_width; + fsl_chan->fsc.burst = cfg->dst_maxburst; + fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width); + } else + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(fsl_edma_slave_config); + +static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan, + struct virt_dma_desc *vdesc, bool in_progress) +{ + struct fsl_edma_desc *edesc = fsl_chan->edesc; + void __iomem *addr = fsl_chan->edma->membase; + u32 ch = fsl_chan->vchan.chan.chan_id; + enum dma_transfer_direction dir = fsl_chan->fsc.dir; + dma_addr_t cur_addr, dma_addr; + size_t len, size; + int i; + + /* calculate the total size in this desc */ + for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++) + len += le32_to_cpu(edesc->tcd[i].vtcd->nbytes) + * le16_to_cpu(edesc->tcd[i].vtcd->biter); + + if (!in_progress) + return len; + + if (dir == DMA_MEM_TO_DEV) + cur_addr = edma_readl( + fsl_chan->edma, addr + EDMA_TCD_SADDR(ch)); + else + cur_addr = edma_readl( + fsl_chan->edma, addr + EDMA_TCD_DADDR(ch)); + + /* figure out the finished and calculate the residue */ + for (i = 0; i < fsl_chan->edesc->n_tcds; i++) { + size = le32_to_cpu(edesc->tcd[i].vtcd->nbytes) + * le16_to_cpu(edesc->tcd[i].vtcd->biter); + if (dir == DMA_MEM_TO_DEV) + dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->saddr); + else + dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->daddr); + + len -= size; + if (cur_addr >= dma_addr && cur_addr < dma_addr + size) { + len += dma_addr + size - cur_addr; + break; + } + } + + return len; +} + +enum dma_status fsl_edma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct virt_dma_desc *vdesc; + enum dma_status status; + unsigned long flags; + + status = dma_cookie_status(chan, cookie, txstate); + if (status == DMA_COMPLETE) + return status; + + if (!txstate) + return fsl_chan->status; + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + vdesc = vchan_find_desc(&fsl_chan->vchan, cookie); + if (fsl_chan->edesc && cookie == fsl_chan->edesc->vdesc.tx.cookie) + txstate->residue = + fsl_edma_desc_residue(fsl_chan, vdesc, true); + else if (vdesc) + txstate->residue = + fsl_edma_desc_residue(fsl_chan, vdesc, false); + else + txstate->residue = 0; + + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + + return fsl_chan->status; +} +EXPORT_SYMBOL_GPL(fsl_edma_tx_status); + +static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan, + struct fsl_edma_hw_tcd *tcd) +{ + struct fsl_edma_engine *edma = fsl_chan->edma; + void __iomem *addr = fsl_chan->edma->membase; + u32 ch = fsl_chan->vchan.chan.chan_id; + + /* + * TCD parameters are stored in struct fsl_edma_hw_tcd in little + * endian format. However, we need to load the TCD registers in + * big- or little-endian obeying the eDMA engine model endian. + */ + edma_writew(edma, 0, addr + EDMA_TCD_CSR(ch)); + edma_writel(edma, le32_to_cpu(tcd->saddr), addr + EDMA_TCD_SADDR(ch)); + edma_writel(edma, le32_to_cpu(tcd->daddr), addr + EDMA_TCD_DADDR(ch)); + + edma_writew(edma, le16_to_cpu(tcd->attr), addr + EDMA_TCD_ATTR(ch)); + edma_writew(edma, le16_to_cpu(tcd->soff), addr + EDMA_TCD_SOFF(ch)); + + edma_writel(edma, le32_to_cpu(tcd->nbytes), addr + EDMA_TCD_NBYTES(ch)); + edma_writel(edma, le32_to_cpu(tcd->slast), addr + EDMA_TCD_SLAST(ch)); + + edma_writew(edma, le16_to_cpu(tcd->citer), addr + EDMA_TCD_CITER(ch)); + edma_writew(edma, le16_to_cpu(tcd->biter), addr + EDMA_TCD_BITER(ch)); + edma_writew(edma, le16_to_cpu(tcd->doff), addr + EDMA_TCD_DOFF(ch)); + + edma_writel(edma, + le32_to_cpu(tcd->dlast_sga), addr + EDMA_TCD_DLAST_SGA(ch)); + + edma_writew(edma, le16_to_cpu(tcd->csr), addr + EDMA_TCD_CSR(ch)); +} + +static inline +void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst, + u16 attr, u16 soff, u32 nbytes, u32 slast, u16 citer, + u16 biter, u16 doff, u32 dlast_sga, bool major_int, + bool disable_req, bool enable_sg) +{ + u16 csr = 0; + + /* + * eDMA hardware SGs require the TCDs to be stored in little + * endian format irrespective of the register endian model. + * So we put the value in little endian in memory, waiting + * for fsl_edma_set_tcd_regs doing the swap. + */ + tcd->saddr = cpu_to_le32(src); + tcd->daddr = cpu_to_le32(dst); + + tcd->attr = cpu_to_le16(attr); + + tcd->soff = cpu_to_le16(EDMA_TCD_SOFF_SOFF(soff)); + + tcd->nbytes = cpu_to_le32(EDMA_TCD_NBYTES_NBYTES(nbytes)); + tcd->slast = cpu_to_le32(EDMA_TCD_SLAST_SLAST(slast)); + + tcd->citer = cpu_to_le16(EDMA_TCD_CITER_CITER(citer)); + tcd->doff = cpu_to_le16(EDMA_TCD_DOFF_DOFF(doff)); + + tcd->dlast_sga = cpu_to_le32(EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga)); + + tcd->biter = cpu_to_le16(EDMA_TCD_BITER_BITER(biter)); + if (major_int) + csr |= EDMA_TCD_CSR_INT_MAJOR; + + if (disable_req) + csr |= EDMA_TCD_CSR_D_REQ; + + if (enable_sg) + csr |= EDMA_TCD_CSR_E_SG; + + tcd->csr = cpu_to_le16(csr); +} + +static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan, + int sg_len) +{ + struct fsl_edma_desc *fsl_desc; + int i; + + fsl_desc = kzalloc(sizeof(*fsl_desc) + + sizeof(struct fsl_edma_sw_tcd) * + sg_len, GFP_NOWAIT); + if (!fsl_desc) + return NULL; + + fsl_desc->echan = fsl_chan; + fsl_desc->n_tcds = sg_len; + for (i = 0; i < sg_len; i++) { + fsl_desc->tcd[i].vtcd = dma_pool_alloc(fsl_chan->tcd_pool, + GFP_NOWAIT, &fsl_desc->tcd[i].ptcd); + if (!fsl_desc->tcd[i].vtcd) + goto err; + } + return fsl_desc; + +err: + while (--i >= 0) + dma_pool_free(fsl_chan->tcd_pool, fsl_desc->tcd[i].vtcd, + fsl_desc->tcd[i].ptcd); + kfree(fsl_desc); + return NULL; +} + +struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct fsl_edma_desc *fsl_desc; + dma_addr_t dma_buf_next; + int sg_len, i; + u32 src_addr, dst_addr, last_sg, nbytes; + u16 soff, doff, iter; + + if (!is_slave_direction(fsl_chan->fsc.dir)) + return NULL; + + sg_len = buf_len / period_len; + fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); + if (!fsl_desc) + return NULL; + fsl_desc->iscyclic = true; + + dma_buf_next = dma_addr; + nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst; + iter = period_len / nbytes; + + for (i = 0; i < sg_len; i++) { + if (dma_buf_next >= dma_addr + buf_len) + dma_buf_next = dma_addr; + + /* get next sg's physical address */ + last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; + + if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) { + src_addr = dma_buf_next; + dst_addr = fsl_chan->fsc.dev_addr; + soff = fsl_chan->fsc.addr_width; + doff = 0; + } else { + src_addr = fsl_chan->fsc.dev_addr; + dst_addr = dma_buf_next; + soff = 0; + doff = fsl_chan->fsc.addr_width; + } + + fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, dst_addr, + fsl_chan->fsc.attr, soff, nbytes, 0, iter, + iter, doff, last_sg, true, false, true); + dma_buf_next += period_len; + } + + return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); +} +EXPORT_SYMBOL_GPL(fsl_edma_prep_dma_cyclic); + +struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct fsl_edma_desc *fsl_desc; + struct scatterlist *sg; + u32 src_addr, dst_addr, last_sg, nbytes; + u16 soff, doff, iter; + int i; + + if (!is_slave_direction(fsl_chan->fsc.dir)) + return NULL; + + fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); + if (!fsl_desc) + return NULL; + fsl_desc->iscyclic = false; + + nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst; + for_each_sg(sgl, sg, sg_len, i) { + /* get next sg's physical address */ + last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; + + if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) { + src_addr = sg_dma_address(sg); + dst_addr = fsl_chan->fsc.dev_addr; + soff = fsl_chan->fsc.addr_width; + doff = 0; + } else { + src_addr = fsl_chan->fsc.dev_addr; + dst_addr = sg_dma_address(sg); + soff = 0; + doff = fsl_chan->fsc.addr_width; + } + + iter = sg_dma_len(sg) / nbytes; + if (i < sg_len - 1) { + last_sg = fsl_desc->tcd[(i + 1)].ptcd; + fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, + dst_addr, fsl_chan->fsc.attr, soff, + nbytes, 0, iter, iter, doff, last_sg, + false, false, true); + } else { + last_sg = 0; + fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, + dst_addr, fsl_chan->fsc.attr, soff, + nbytes, 0, iter, iter, doff, last_sg, + true, true, false); + } + } + + return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); +} +EXPORT_SYMBOL_GPL(fsl_edma_prep_slave_sg); + +void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan) +{ + struct virt_dma_desc *vdesc; + + vdesc = vchan_next_desc(&fsl_chan->vchan); + if (!vdesc) + return; + fsl_chan->edesc = to_fsl_edma_desc(vdesc); + fsl_edma_set_tcd_regs(fsl_chan, fsl_chan->edesc->tcd[0].vtcd); + fsl_edma_enable_request(fsl_chan); + fsl_chan->status = DMA_IN_PROGRESS; + fsl_chan->idle = false; +} +EXPORT_SYMBOL_GPL(fsl_edma_xfer_desc); + +void fsl_edma_issue_pending(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + + if (unlikely(fsl_chan->pm_state != RUNNING)) { + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + /* cannot submit due to suspend */ + return; + } + + if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc) + fsl_edma_xfer_desc(fsl_chan); + + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); +} +EXPORT_SYMBOL_GPL(fsl_edma_issue_pending); + +int fsl_edma_alloc_chan_resources(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + + fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev, + sizeof(struct fsl_edma_hw_tcd), + 32, 0); + return 0; +} +EXPORT_SYMBOL_GPL(fsl_edma_alloc_chan_resources); + +void fsl_edma_free_chan_resources(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + fsl_edma_disable_request(fsl_chan); + fsl_edma_chan_mux(fsl_chan, 0, false); + fsl_chan->edesc = NULL; + vchan_get_all_descriptors(&fsl_chan->vchan, &head); + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + + vchan_dma_desc_free_list(&fsl_chan->vchan, &head); + dma_pool_destroy(fsl_chan->tcd_pool); + fsl_chan->tcd_pool = NULL; +} +EXPORT_SYMBOL_GPL(fsl_edma_free_chan_resources); + +void fsl_edma_cleanup_vchan(struct dma_device *dmadev) +{ + struct fsl_edma_chan *chan, *_chan; + + list_for_each_entry_safe(chan, _chan, + &dmadev->channels, vchan.chan.device_node) { + list_del(&chan->vchan.chan.device_node); + tasklet_kill(&chan->vchan.task); + } +} +EXPORT_SYMBOL_GPL(fsl_edma_cleanup_vchan); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h new file mode 100644 index 000000000000..5458dd3b6da8 --- /dev/null +++ b/drivers/dma/fsl-edma-common.h @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2013-2014 Freescale Semiconductor, Inc. + * Copyright 2018 Angelo Dureghello + */ +#ifndef _FSL_EDMA_COMMON_H_ +#define _FSL_EDMA_COMMON_H_ + +#include "virt-dma.h" + +#define EDMA_CR 0x00 +#define EDMA_ES 0x04 +#define EDMA_ERQ 0x0C +#define EDMA_EEI 0x14 +#define EDMA_SERQ 0x1B +#define EDMA_CERQ 0x1A +#define EDMA_SEEI 0x19 +#define EDMA_CEEI 0x18 +#define EDMA_CINT 0x1F +#define EDMA_CERR 0x1E +#define EDMA_SSRT 0x1D +#define EDMA_CDNE 0x1C +#define EDMA_INTR 0x24 +#define EDMA_ERR 0x2C + +#define EDMA_TCD_SADDR(x) (0x1000 + 32 * (x)) +#define EDMA_TCD_SOFF(x) (0x1004 + 32 * (x)) +#define EDMA_TCD_ATTR(x) (0x1006 + 32 * (x)) +#define EDMA_TCD_NBYTES(x) (0x1008 + 32 * (x)) +#define EDMA_TCD_SLAST(x) (0x100C + 32 * (x)) +#define EDMA_TCD_DADDR(x) (0x1010 + 32 * (x)) +#define EDMA_TCD_DOFF(x) (0x1014 + 32 * (x)) +#define EDMA_TCD_CITER_ELINK(x) (0x1016 + 32 * (x)) +#define EDMA_TCD_CITER(x) (0x1016 + 32 * (x)) +#define EDMA_TCD_DLAST_SGA(x) (0x1018 + 32 * (x)) +#define EDMA_TCD_CSR(x) (0x101C + 32 * (x)) +#define EDMA_TCD_BITER_ELINK(x) (0x101E + 32 * (x)) +#define EDMA_TCD_BITER(x) (0x101E + 32 * (x)) + +#define EDMA_CR_EDBG BIT(1) +#define EDMA_CR_ERCA BIT(2) +#define EDMA_CR_ERGA BIT(3) +#define EDMA_CR_HOE BIT(4) +#define EDMA_CR_HALT BIT(5) +#define EDMA_CR_CLM BIT(6) +#define EDMA_CR_EMLM BIT(7) +#define EDMA_CR_ECX BIT(16) +#define EDMA_CR_CX BIT(17) + +#define EDMA_SEEI_SEEI(x) ((x) & 0x1F) +#define EDMA_CEEI_CEEI(x) ((x) & 0x1F) +#define EDMA_CINT_CINT(x) ((x) & 0x1F) +#define EDMA_CERR_CERR(x) ((x) & 0x1F) + +#define EDMA_TCD_ATTR_DSIZE(x) (((x) & 0x0007)) +#define EDMA_TCD_ATTR_DMOD(x) (((x) & 0x001F) << 3) +#define EDMA_TCD_ATTR_SSIZE(x) (((x) & 0x0007) << 8) +#define EDMA_TCD_ATTR_SMOD(x) (((x) & 0x001F) << 11) +#define EDMA_TCD_ATTR_SSIZE_8BIT (0x0000) +#define EDMA_TCD_ATTR_SSIZE_16BIT (0x0100) +#define EDMA_TCD_ATTR_SSIZE_32BIT (0x0200) +#define EDMA_TCD_ATTR_SSIZE_64BIT (0x0300) +#define EDMA_TCD_ATTR_SSIZE_32BYTE (0x0500) +#define EDMA_TCD_ATTR_DSIZE_8BIT (0x0000) +#define EDMA_TCD_ATTR_DSIZE_16BIT (0x0001) +#define EDMA_TCD_ATTR_DSIZE_32BIT (0x0002) +#define EDMA_TCD_ATTR_DSIZE_64BIT (0x0003) +#define EDMA_TCD_ATTR_DSIZE_32BYTE (0x0005) + +#define EDMA_TCD_SOFF_SOFF(x) (x) +#define EDMA_TCD_NBYTES_NBYTES(x) (x) +#define EDMA_TCD_SLAST_SLAST(x) (x) +#define EDMA_TCD_DADDR_DADDR(x) (x) +#define EDMA_TCD_CITER_CITER(x) ((x) & 0x7FFF) +#define EDMA_TCD_DOFF_DOFF(x) (x) +#define EDMA_TCD_DLAST_SGA_DLAST_SGA(x) (x) +#define EDMA_TCD_BITER_BITER(x) ((x) & 0x7FFF) + +#define EDMA_TCD_CSR_START BIT(0) +#define EDMA_TCD_CSR_INT_MAJOR BIT(1) +#define EDMA_TCD_CSR_INT_HALF BIT(2) +#define EDMA_TCD_CSR_D_REQ BIT(3) +#define EDMA_TCD_CSR_E_SG BIT(4) +#define EDMA_TCD_CSR_E_LINK BIT(5) +#define EDMA_TCD_CSR_ACTIVE BIT(6) +#define EDMA_TCD_CSR_DONE BIT(7) + +#define EDMAMUX_CHCFG_DIS 0x0 +#define EDMAMUX_CHCFG_ENBL 0x80 +#define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F) + +#define DMAMUX_NR 2 + +#define FSL_EDMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)) +enum fsl_edma_pm_state { + RUNNING = 0, + SUSPENDED, +}; + +struct fsl_edma_hw_tcd { + __le32 saddr; + __le16 soff; + __le16 attr; + __le32 nbytes; + __le32 slast; + __le32 daddr; + __le16 doff; + __le16 citer; + __le32 dlast_sga; + __le16 csr; + __le16 biter; +}; + +struct fsl_edma_sw_tcd { + dma_addr_t ptcd; + struct fsl_edma_hw_tcd *vtcd; +}; + +struct fsl_edma_slave_config { + enum dma_transfer_direction dir; + enum dma_slave_buswidth addr_width; + u32 dev_addr; + u32 burst; + u32 attr; +}; + +struct fsl_edma_chan { + struct virt_dma_chan vchan; + enum dma_status status; + enum fsl_edma_pm_state pm_state; + bool idle; + u32 slave_id; + struct fsl_edma_engine *edma; + struct fsl_edma_desc *edesc; + struct fsl_edma_slave_config fsc; + struct dma_pool *tcd_pool; +}; + +struct fsl_edma_desc { + struct virt_dma_desc vdesc; + struct fsl_edma_chan *echan; + bool iscyclic; + unsigned int n_tcds; + struct fsl_edma_sw_tcd tcd[]; +}; + +struct fsl_edma_engine { + struct dma_device dma_dev; + void __iomem *membase; + void __iomem *muxbase[DMAMUX_NR]; + struct clk *muxclk[DMAMUX_NR]; + struct mutex fsl_edma_mutex; + u32 n_chans; + int txirq; + int errirq; + bool big_endian; + struct fsl_edma_chan chans[]; +}; + +/* + * R/W functions for big- or little-endian registers: + * The eDMA controller's endian is independent of the CPU core's endian. + * For the big-endian IP module, the offset for 8-bit or 16-bit registers + * should also be swapped opposite to that in little-endian IP. + */ +static inline u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr) +{ + if (edma->big_endian) + return ioread32be(addr); + else + return ioread32(addr); +} + +static inline void edma_writeb(struct fsl_edma_engine *edma, + u8 val, void __iomem *addr) +{ + /* swap the reg offset for these in big-endian mode */ + if (edma->big_endian) + iowrite8(val, (void __iomem *)((unsigned long)addr ^ 0x3)); + else + iowrite8(val, addr); +} + +static inline void edma_writew(struct fsl_edma_engine *edma, + u16 val, void __iomem *addr) +{ + /* swap the reg offset for these in big-endian mode */ + if (edma->big_endian) + iowrite16be(val, (void __iomem *)((unsigned long)addr ^ 0x2)); + else + iowrite16(val, addr); +} + +static inline void edma_writel(struct fsl_edma_engine *edma, + u32 val, void __iomem *addr) +{ + if (edma->big_endian) + iowrite32be(val, addr); + else + iowrite32(val, addr); +} + +static inline struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct fsl_edma_chan, vchan.chan); +} + +static inline struct fsl_edma_desc *to_fsl_edma_desc(struct virt_dma_desc *vd) +{ + return container_of(vd, struct fsl_edma_desc, vdesc); +} + +void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan); +void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan, + unsigned int slot, bool enable); +void fsl_edma_free_desc(struct virt_dma_desc *vdesc); +int fsl_edma_terminate_all(struct dma_chan *chan); +int fsl_edma_pause(struct dma_chan *chan); +int fsl_edma_resume(struct dma_chan *chan); +int fsl_edma_slave_config(struct dma_chan *chan, + struct dma_slave_config *cfg); +enum dma_status fsl_edma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate); +struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags); +struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context); +void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan); +void fsl_edma_issue_pending(struct dma_chan *chan); +int fsl_edma_alloc_chan_resources(struct dma_chan *chan); +void fsl_edma_free_chan_resources(struct dma_chan *chan); +void fsl_edma_cleanup_vchan(struct dma_device *dmadev); + +#endif /* _FSL_EDMA_COMMON_H_ */ diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index c7568869284e..337ad0c0ea78 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -13,653 +13,16 @@ * option) any later version. */ -#include #include #include #include -#include -#include -#include -#include #include #include #include #include #include -#include "virt-dma.h" - -#define EDMA_CR 0x00 -#define EDMA_ES 0x04 -#define EDMA_ERQ 0x0C -#define EDMA_EEI 0x14 -#define EDMA_SERQ 0x1B -#define EDMA_CERQ 0x1A -#define EDMA_SEEI 0x19 -#define EDMA_CEEI 0x18 -#define EDMA_CINT 0x1F -#define EDMA_CERR 0x1E -#define EDMA_SSRT 0x1D -#define EDMA_CDNE 0x1C -#define EDMA_INTR 0x24 -#define EDMA_ERR 0x2C - -#define EDMA_TCD_SADDR(x) (0x1000 + 32 * (x)) -#define EDMA_TCD_SOFF(x) (0x1004 + 32 * (x)) -#define EDMA_TCD_ATTR(x) (0x1006 + 32 * (x)) -#define EDMA_TCD_NBYTES(x) (0x1008 + 32 * (x)) -#define EDMA_TCD_SLAST(x) (0x100C + 32 * (x)) -#define EDMA_TCD_DADDR(x) (0x1010 + 32 * (x)) -#define EDMA_TCD_DOFF(x) (0x1014 + 32 * (x)) -#define EDMA_TCD_CITER_ELINK(x) (0x1016 + 32 * (x)) -#define EDMA_TCD_CITER(x) (0x1016 + 32 * (x)) -#define EDMA_TCD_DLAST_SGA(x) (0x1018 + 32 * (x)) -#define EDMA_TCD_CSR(x) (0x101C + 32 * (x)) -#define EDMA_TCD_BITER_ELINK(x) (0x101E + 32 * (x)) -#define EDMA_TCD_BITER(x) (0x101E + 32 * (x)) - -#define EDMA_CR_EDBG BIT(1) -#define EDMA_CR_ERCA BIT(2) -#define EDMA_CR_ERGA BIT(3) -#define EDMA_CR_HOE BIT(4) -#define EDMA_CR_HALT BIT(5) -#define EDMA_CR_CLM BIT(6) -#define EDMA_CR_EMLM BIT(7) -#define EDMA_CR_ECX BIT(16) -#define EDMA_CR_CX BIT(17) - -#define EDMA_SEEI_SEEI(x) ((x) & 0x1F) -#define EDMA_CEEI_CEEI(x) ((x) & 0x1F) -#define EDMA_CINT_CINT(x) ((x) & 0x1F) -#define EDMA_CERR_CERR(x) ((x) & 0x1F) - -#define EDMA_TCD_ATTR_DSIZE(x) (((x) & 0x0007)) -#define EDMA_TCD_ATTR_DMOD(x) (((x) & 0x001F) << 3) -#define EDMA_TCD_ATTR_SSIZE(x) (((x) & 0x0007) << 8) -#define EDMA_TCD_ATTR_SMOD(x) (((x) & 0x001F) << 11) -#define EDMA_TCD_ATTR_SSIZE_8BIT (0x0000) -#define EDMA_TCD_ATTR_SSIZE_16BIT (0x0100) -#define EDMA_TCD_ATTR_SSIZE_32BIT (0x0200) -#define EDMA_TCD_ATTR_SSIZE_64BIT (0x0300) -#define EDMA_TCD_ATTR_SSIZE_32BYTE (0x0500) -#define EDMA_TCD_ATTR_DSIZE_8BIT (0x0000) -#define EDMA_TCD_ATTR_DSIZE_16BIT (0x0001) -#define EDMA_TCD_ATTR_DSIZE_32BIT (0x0002) -#define EDMA_TCD_ATTR_DSIZE_64BIT (0x0003) -#define EDMA_TCD_ATTR_DSIZE_32BYTE (0x0005) - -#define EDMA_TCD_SOFF_SOFF(x) (x) -#define EDMA_TCD_NBYTES_NBYTES(x) (x) -#define EDMA_TCD_SLAST_SLAST(x) (x) -#define EDMA_TCD_DADDR_DADDR(x) (x) -#define EDMA_TCD_CITER_CITER(x) ((x) & 0x7FFF) -#define EDMA_TCD_DOFF_DOFF(x) (x) -#define EDMA_TCD_DLAST_SGA_DLAST_SGA(x) (x) -#define EDMA_TCD_BITER_BITER(x) ((x) & 0x7FFF) - -#define EDMA_TCD_CSR_START BIT(0) -#define EDMA_TCD_CSR_INT_MAJOR BIT(1) -#define EDMA_TCD_CSR_INT_HALF BIT(2) -#define EDMA_TCD_CSR_D_REQ BIT(3) -#define EDMA_TCD_CSR_E_SG BIT(4) -#define EDMA_TCD_CSR_E_LINK BIT(5) -#define EDMA_TCD_CSR_ACTIVE BIT(6) -#define EDMA_TCD_CSR_DONE BIT(7) - -#define EDMAMUX_CHCFG_DIS 0x0 -#define EDMAMUX_CHCFG_ENBL 0x80 -#define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F) - -#define DMAMUX_NR 2 - -#define FSL_EDMA_BUSWIDTHS BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ - BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ - BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ - BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) -enum fsl_edma_pm_state { - RUNNING = 0, - SUSPENDED, -}; - -struct fsl_edma_hw_tcd { - __le32 saddr; - __le16 soff; - __le16 attr; - __le32 nbytes; - __le32 slast; - __le32 daddr; - __le16 doff; - __le16 citer; - __le32 dlast_sga; - __le16 csr; - __le16 biter; -}; - -struct fsl_edma_sw_tcd { - dma_addr_t ptcd; - struct fsl_edma_hw_tcd *vtcd; -}; - -struct fsl_edma_slave_config { - enum dma_transfer_direction dir; - enum dma_slave_buswidth addr_width; - u32 dev_addr; - u32 burst; - u32 attr; -}; - -struct fsl_edma_chan { - struct virt_dma_chan vchan; - enum dma_status status; - enum fsl_edma_pm_state pm_state; - bool idle; - u32 slave_id; - struct fsl_edma_engine *edma; - struct fsl_edma_desc *edesc; - struct fsl_edma_slave_config fsc; - struct dma_pool *tcd_pool; -}; - -struct fsl_edma_desc { - struct virt_dma_desc vdesc; - struct fsl_edma_chan *echan; - bool iscyclic; - unsigned int n_tcds; - struct fsl_edma_sw_tcd tcd[]; -}; - -struct fsl_edma_engine { - struct dma_device dma_dev; - void __iomem *membase; - void __iomem *muxbase[DMAMUX_NR]; - struct clk *muxclk[DMAMUX_NR]; - struct mutex fsl_edma_mutex; - u32 n_chans; - int txirq; - int errirq; - bool big_endian; - struct fsl_edma_chan chans[]; -}; - -/* - * R/W functions for big- or little-endian registers: - * The eDMA controller's endian is independent of the CPU core's endian. - * For the big-endian IP module, the offset for 8-bit or 16-bit registers - * should also be swapped opposite to that in little-endian IP. - */ - -static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr) -{ - if (edma->big_endian) - return ioread32be(addr); - else - return ioread32(addr); -} - -static void edma_writeb(struct fsl_edma_engine *edma, u8 val, void __iomem *addr) -{ - /* swap the reg offset for these in big-endian mode */ - if (edma->big_endian) - iowrite8(val, (void __iomem *)((unsigned long)addr ^ 0x3)); - else - iowrite8(val, addr); -} - -static void edma_writew(struct fsl_edma_engine *edma, u16 val, void __iomem *addr) -{ - /* swap the reg offset for these in big-endian mode */ - if (edma->big_endian) - iowrite16be(val, (void __iomem *)((unsigned long)addr ^ 0x2)); - else - iowrite16(val, addr); -} - -static void edma_writel(struct fsl_edma_engine *edma, u32 val, void __iomem *addr) -{ - if (edma->big_endian) - iowrite32be(val, addr); - else - iowrite32(val, addr); -} - -static struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan) -{ - return container_of(chan, struct fsl_edma_chan, vchan.chan); -} - -static struct fsl_edma_desc *to_fsl_edma_desc(struct virt_dma_desc *vd) -{ - return container_of(vd, struct fsl_edma_desc, vdesc); -} - -static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan) -{ - void __iomem *addr = fsl_chan->edma->membase; - u32 ch = fsl_chan->vchan.chan.chan_id; - - edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), addr + EDMA_SEEI); - edma_writeb(fsl_chan->edma, ch, addr + EDMA_SERQ); -} - -static void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan) -{ - void __iomem *addr = fsl_chan->edma->membase; - u32 ch = fsl_chan->vchan.chan.chan_id; - - edma_writeb(fsl_chan->edma, ch, addr + EDMA_CERQ); - edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), addr + EDMA_CEEI); -} - -static void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan, - unsigned int slot, bool enable) -{ - u32 ch = fsl_chan->vchan.chan.chan_id; - void __iomem *muxaddr; - unsigned chans_per_mux, ch_off; - - chans_per_mux = fsl_chan->edma->n_chans / DMAMUX_NR; - ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux; - muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux]; - slot = EDMAMUX_CHCFG_SOURCE(slot); - - if (enable) - iowrite8(EDMAMUX_CHCFG_ENBL | slot, muxaddr + ch_off); - else - iowrite8(EDMAMUX_CHCFG_DIS, muxaddr + ch_off); -} - -static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width) -{ - switch (addr_width) { - case 1: - return EDMA_TCD_ATTR_SSIZE_8BIT | EDMA_TCD_ATTR_DSIZE_8BIT; - case 2: - return EDMA_TCD_ATTR_SSIZE_16BIT | EDMA_TCD_ATTR_DSIZE_16BIT; - case 4: - return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT; - case 8: - return EDMA_TCD_ATTR_SSIZE_64BIT | EDMA_TCD_ATTR_DSIZE_64BIT; - default: - return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT; - } -} - -static void fsl_edma_free_desc(struct virt_dma_desc *vdesc) -{ - struct fsl_edma_desc *fsl_desc; - int i; - - fsl_desc = to_fsl_edma_desc(vdesc); - for (i = 0; i < fsl_desc->n_tcds; i++) - dma_pool_free(fsl_desc->echan->tcd_pool, fsl_desc->tcd[i].vtcd, - fsl_desc->tcd[i].ptcd); - kfree(fsl_desc); -} - -static int fsl_edma_terminate_all(struct dma_chan *chan) -{ - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); - unsigned long flags; - LIST_HEAD(head); - - spin_lock_irqsave(&fsl_chan->vchan.lock, flags); - fsl_edma_disable_request(fsl_chan); - fsl_chan->edesc = NULL; - fsl_chan->idle = true; - vchan_get_all_descriptors(&fsl_chan->vchan, &head); - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); - vchan_dma_desc_free_list(&fsl_chan->vchan, &head); - return 0; -} - -static int fsl_edma_pause(struct dma_chan *chan) -{ - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); - unsigned long flags; - - spin_lock_irqsave(&fsl_chan->vchan.lock, flags); - if (fsl_chan->edesc) { - fsl_edma_disable_request(fsl_chan); - fsl_chan->status = DMA_PAUSED; - fsl_chan->idle = true; - } - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); - return 0; -} - -static int fsl_edma_resume(struct dma_chan *chan) -{ - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); - unsigned long flags; - - spin_lock_irqsave(&fsl_chan->vchan.lock, flags); - if (fsl_chan->edesc) { - fsl_edma_enable_request(fsl_chan); - fsl_chan->status = DMA_IN_PROGRESS; - fsl_chan->idle = false; - } - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); - return 0; -} - -static int fsl_edma_slave_config(struct dma_chan *chan, - struct dma_slave_config *cfg) -{ - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); - - fsl_chan->fsc.dir = cfg->direction; - if (cfg->direction == DMA_DEV_TO_MEM) { - fsl_chan->fsc.dev_addr = cfg->src_addr; - fsl_chan->fsc.addr_width = cfg->src_addr_width; - fsl_chan->fsc.burst = cfg->src_maxburst; - fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width); - } else if (cfg->direction == DMA_MEM_TO_DEV) { - fsl_chan->fsc.dev_addr = cfg->dst_addr; - fsl_chan->fsc.addr_width = cfg->dst_addr_width; - fsl_chan->fsc.burst = cfg->dst_maxburst; - fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width); - } else { - return -EINVAL; - } - return 0; -} - -static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan, - struct virt_dma_desc *vdesc, bool in_progress) -{ - struct fsl_edma_desc *edesc = fsl_chan->edesc; - void __iomem *addr = fsl_chan->edma->membase; - u32 ch = fsl_chan->vchan.chan.chan_id; - enum dma_transfer_direction dir = fsl_chan->fsc.dir; - dma_addr_t cur_addr, dma_addr; - size_t len, size; - int i; - - /* calculate the total size in this desc */ - for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++) - len += le32_to_cpu(edesc->tcd[i].vtcd->nbytes) - * le16_to_cpu(edesc->tcd[i].vtcd->biter); - - if (!in_progress) - return len; - - if (dir == DMA_MEM_TO_DEV) - cur_addr = edma_readl(fsl_chan->edma, addr + EDMA_TCD_SADDR(ch)); - else - cur_addr = edma_readl(fsl_chan->edma, addr + EDMA_TCD_DADDR(ch)); - - /* figure out the finished and calculate the residue */ - for (i = 0; i < fsl_chan->edesc->n_tcds; i++) { - size = le32_to_cpu(edesc->tcd[i].vtcd->nbytes) - * le16_to_cpu(edesc->tcd[i].vtcd->biter); - if (dir == DMA_MEM_TO_DEV) - dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->saddr); - else - dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->daddr); - - len -= size; - if (cur_addr >= dma_addr && cur_addr < dma_addr + size) { - len += dma_addr + size - cur_addr; - break; - } - } - - return len; -} - -static enum dma_status fsl_edma_tx_status(struct dma_chan *chan, - dma_cookie_t cookie, struct dma_tx_state *txstate) -{ - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); - struct virt_dma_desc *vdesc; - enum dma_status status; - unsigned long flags; - - status = dma_cookie_status(chan, cookie, txstate); - if (status == DMA_COMPLETE) - return status; - - if (!txstate) - return fsl_chan->status; - - spin_lock_irqsave(&fsl_chan->vchan.lock, flags); - vdesc = vchan_find_desc(&fsl_chan->vchan, cookie); - if (fsl_chan->edesc && cookie == fsl_chan->edesc->vdesc.tx.cookie) - txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, true); - else if (vdesc) - txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, false); - else - txstate->residue = 0; - - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); - - return fsl_chan->status; -} - -static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan, - struct fsl_edma_hw_tcd *tcd) -{ - struct fsl_edma_engine *edma = fsl_chan->edma; - void __iomem *addr = fsl_chan->edma->membase; - u32 ch = fsl_chan->vchan.chan.chan_id; - - /* - * TCD parameters are stored in struct fsl_edma_hw_tcd in little - * endian format. However, we need to load the TCD registers in - * big- or little-endian obeying the eDMA engine model endian. - */ - edma_writew(edma, 0, addr + EDMA_TCD_CSR(ch)); - edma_writel(edma, le32_to_cpu(tcd->saddr), addr + EDMA_TCD_SADDR(ch)); - edma_writel(edma, le32_to_cpu(tcd->daddr), addr + EDMA_TCD_DADDR(ch)); - - edma_writew(edma, le16_to_cpu(tcd->attr), addr + EDMA_TCD_ATTR(ch)); - edma_writew(edma, le16_to_cpu(tcd->soff), addr + EDMA_TCD_SOFF(ch)); - - edma_writel(edma, le32_to_cpu(tcd->nbytes), addr + EDMA_TCD_NBYTES(ch)); - edma_writel(edma, le32_to_cpu(tcd->slast), addr + EDMA_TCD_SLAST(ch)); - - edma_writew(edma, le16_to_cpu(tcd->citer), addr + EDMA_TCD_CITER(ch)); - edma_writew(edma, le16_to_cpu(tcd->biter), addr + EDMA_TCD_BITER(ch)); - edma_writew(edma, le16_to_cpu(tcd->doff), addr + EDMA_TCD_DOFF(ch)); - - edma_writel(edma, le32_to_cpu(tcd->dlast_sga), addr + EDMA_TCD_DLAST_SGA(ch)); - - edma_writew(edma, le16_to_cpu(tcd->csr), addr + EDMA_TCD_CSR(ch)); -} - -static inline -void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst, - u16 attr, u16 soff, u32 nbytes, u32 slast, u16 citer, - u16 biter, u16 doff, u32 dlast_sga, bool major_int, - bool disable_req, bool enable_sg) -{ - u16 csr = 0; - - /* - * eDMA hardware SGs require the TCDs to be stored in little - * endian format irrespective of the register endian model. - * So we put the value in little endian in memory, waiting - * for fsl_edma_set_tcd_regs doing the swap. - */ - tcd->saddr = cpu_to_le32(src); - tcd->daddr = cpu_to_le32(dst); - - tcd->attr = cpu_to_le16(attr); - - tcd->soff = cpu_to_le16(EDMA_TCD_SOFF_SOFF(soff)); - - tcd->nbytes = cpu_to_le32(EDMA_TCD_NBYTES_NBYTES(nbytes)); - tcd->slast = cpu_to_le32(EDMA_TCD_SLAST_SLAST(slast)); - - tcd->citer = cpu_to_le16(EDMA_TCD_CITER_CITER(citer)); - tcd->doff = cpu_to_le16(EDMA_TCD_DOFF_DOFF(doff)); - - tcd->dlast_sga = cpu_to_le32(EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga)); - - tcd->biter = cpu_to_le16(EDMA_TCD_BITER_BITER(biter)); - if (major_int) - csr |= EDMA_TCD_CSR_INT_MAJOR; - - if (disable_req) - csr |= EDMA_TCD_CSR_D_REQ; - - if (enable_sg) - csr |= EDMA_TCD_CSR_E_SG; - - tcd->csr = cpu_to_le16(csr); -} - -static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan, - int sg_len) -{ - struct fsl_edma_desc *fsl_desc; - int i; - - fsl_desc = kzalloc(sizeof(*fsl_desc) + sizeof(struct fsl_edma_sw_tcd) * sg_len, - GFP_NOWAIT); - if (!fsl_desc) - return NULL; - - fsl_desc->echan = fsl_chan; - fsl_desc->n_tcds = sg_len; - for (i = 0; i < sg_len; i++) { - fsl_desc->tcd[i].vtcd = dma_pool_alloc(fsl_chan->tcd_pool, - GFP_NOWAIT, &fsl_desc->tcd[i].ptcd); - if (!fsl_desc->tcd[i].vtcd) - goto err; - } - return fsl_desc; - -err: - while (--i >= 0) - dma_pool_free(fsl_chan->tcd_pool, fsl_desc->tcd[i].vtcd, - fsl_desc->tcd[i].ptcd); - kfree(fsl_desc); - return NULL; -} - -static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( - struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, - size_t period_len, enum dma_transfer_direction direction, - unsigned long flags) -{ - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); - struct fsl_edma_desc *fsl_desc; - dma_addr_t dma_buf_next; - int sg_len, i; - u32 src_addr, dst_addr, last_sg, nbytes; - u16 soff, doff, iter; - - if (!is_slave_direction(fsl_chan->fsc.dir)) - return NULL; - - sg_len = buf_len / period_len; - fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); - if (!fsl_desc) - return NULL; - fsl_desc->iscyclic = true; - - dma_buf_next = dma_addr; - nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst; - iter = period_len / nbytes; - - for (i = 0; i < sg_len; i++) { - if (dma_buf_next >= dma_addr + buf_len) - dma_buf_next = dma_addr; - - /* get next sg's physical address */ - last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; - - if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) { - src_addr = dma_buf_next; - dst_addr = fsl_chan->fsc.dev_addr; - soff = fsl_chan->fsc.addr_width; - doff = 0; - } else { - src_addr = fsl_chan->fsc.dev_addr; - dst_addr = dma_buf_next; - soff = 0; - doff = fsl_chan->fsc.addr_width; - } - - fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, dst_addr, - fsl_chan->fsc.attr, soff, nbytes, 0, iter, - iter, doff, last_sg, true, false, true); - dma_buf_next += period_len; - } - - return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); -} - -static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( - struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags, void *context) -{ - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); - struct fsl_edma_desc *fsl_desc; - struct scatterlist *sg; - u32 src_addr, dst_addr, last_sg, nbytes; - u16 soff, doff, iter; - int i; - - if (!is_slave_direction(fsl_chan->fsc.dir)) - return NULL; - - fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); - if (!fsl_desc) - return NULL; - fsl_desc->iscyclic = false; - - nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst; - for_each_sg(sgl, sg, sg_len, i) { - /* get next sg's physical address */ - last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; - - if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) { - src_addr = sg_dma_address(sg); - dst_addr = fsl_chan->fsc.dev_addr; - soff = fsl_chan->fsc.addr_width; - doff = 0; - } else { - src_addr = fsl_chan->fsc.dev_addr; - dst_addr = sg_dma_address(sg); - soff = 0; - doff = fsl_chan->fsc.addr_width; - } - - iter = sg_dma_len(sg) / nbytes; - if (i < sg_len - 1) { - last_sg = fsl_desc->tcd[(i + 1)].ptcd; - fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, - dst_addr, fsl_chan->fsc.attr, soff, - nbytes, 0, iter, iter, doff, last_sg, - false, false, true); - } else { - last_sg = 0; - fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, - dst_addr, fsl_chan->fsc.attr, soff, - nbytes, 0, iter, iter, doff, last_sg, - true, true, false); - } - } - - return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); -} - -static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan) -{ - struct virt_dma_desc *vdesc; - - vdesc = vchan_next_desc(&fsl_chan->vchan); - if (!vdesc) - return; - fsl_chan->edesc = to_fsl_edma_desc(vdesc); - fsl_edma_set_tcd_regs(fsl_chan, fsl_chan->edesc->tcd[0].vtcd); - fsl_edma_enable_request(fsl_chan); - fsl_chan->status = DMA_IN_PROGRESS; - fsl_chan->idle = false; -} +#include "fsl-edma-common.h" static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id) { @@ -730,25 +93,6 @@ static irqreturn_t fsl_edma_irq_handler(int irq, void *dev_id) return fsl_edma_err_handler(irq, dev_id); } -static void fsl_edma_issue_pending(struct dma_chan *chan) -{ - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); - unsigned long flags; - - spin_lock_irqsave(&fsl_chan->vchan.lock, flags); - - if (unlikely(fsl_chan->pm_state != RUNNING)) { - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); - /* cannot submit due to suspend */ - return; - } - - if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc) - fsl_edma_xfer_desc(fsl_chan); - - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); -} - static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { @@ -781,34 +125,6 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, return NULL; } -static int fsl_edma_alloc_chan_resources(struct dma_chan *chan) -{ - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); - - fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev, - sizeof(struct fsl_edma_hw_tcd), - 32, 0); - return 0; -} - -static void fsl_edma_free_chan_resources(struct dma_chan *chan) -{ - struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); - unsigned long flags; - LIST_HEAD(head); - - spin_lock_irqsave(&fsl_chan->vchan.lock, flags); - fsl_edma_disable_request(fsl_chan); - fsl_edma_chan_mux(fsl_chan, 0, false); - fsl_chan->edesc = NULL; - vchan_get_all_descriptors(&fsl_chan->vchan, &head); - spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); - - vchan_dma_desc_free_list(&fsl_chan->vchan, &head); - dma_pool_destroy(fsl_chan->tcd_pool); - fsl_chan->tcd_pool = NULL; -} - static int fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) { @@ -995,17 +311,6 @@ static int fsl_edma_probe(struct platform_device *pdev) return 0; } -static void fsl_edma_cleanup_vchan(struct dma_device *dmadev) -{ - struct fsl_edma_chan *chan, *_chan; - - list_for_each_entry_safe(chan, _chan, - &dmadev->channels, vchan.chan.device_node) { - list_del(&chan->vchan.chan.device_node); - tasklet_kill(&chan->vchan.task); - } -} - static int fsl_edma_remove(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; -- cgit v1.2.3 From 377eaf3b3c4ad74efed77e846320188fc312a0e2 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Sun, 19 Aug 2018 19:27:14 +0200 Subject: dmaengine: fsl-edma: add edma version and configurable registers This patch adds configurable registers (using __iomem addresses) to allow the use of fsl-edma-common code with slightly different edma module versions, as Vybrid (v1) and ColdFire (v2) are. Signed-off-by: Angelo Dureghello Tested-by: Krzysztof Kozlowski Signed-off-by: Vinod Koul --- drivers/dma/fsl-edma-common.c | 106 ++++++++++++++++++++++++++++++------------ drivers/dma/fsl-edma-common.h | 62 ++++++++++++------------ drivers/dma/fsl-edma.c | 32 +++++++------ 3 files changed, 126 insertions(+), 74 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c index 3fc786b3d03c..227034de256e 100644 --- a/drivers/dma/fsl-edma-common.c +++ b/drivers/dma/fsl-edma-common.c @@ -43,20 +43,20 @@ static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan) { - void __iomem *addr = fsl_chan->edma->membase; + struct edma_regs *regs = &fsl_chan->edma->regs; u32 ch = fsl_chan->vchan.chan.chan_id; - edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), addr + EDMA_SEEI); - edma_writeb(fsl_chan->edma, ch, addr + EDMA_SERQ); + edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei); + edma_writeb(fsl_chan->edma, ch, regs->serq); } void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan) { - void __iomem *addr = fsl_chan->edma->membase; + struct edma_regs *regs = &fsl_chan->edma->regs; u32 ch = fsl_chan->vchan.chan.chan_id; - edma_writeb(fsl_chan->edma, ch, addr + EDMA_CERQ); - edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), addr + EDMA_CEEI); + edma_writeb(fsl_chan->edma, ch, regs->cerq); + edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei); } EXPORT_SYMBOL_GPL(fsl_edma_disable_request); @@ -184,7 +184,7 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan, struct virt_dma_desc *vdesc, bool in_progress) { struct fsl_edma_desc *edesc = fsl_chan->edesc; - void __iomem *addr = fsl_chan->edma->membase; + struct edma_regs *regs = &fsl_chan->edma->regs; u32 ch = fsl_chan->vchan.chan.chan_id; enum dma_transfer_direction dir = fsl_chan->fsc.dir; dma_addr_t cur_addr, dma_addr; @@ -200,11 +200,9 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan, return len; if (dir == DMA_MEM_TO_DEV) - cur_addr = edma_readl( - fsl_chan->edma, addr + EDMA_TCD_SADDR(ch)); + cur_addr = edma_readl(fsl_chan->edma, ®s->tcd[ch].saddr); else - cur_addr = edma_readl( - fsl_chan->edma, addr + EDMA_TCD_DADDR(ch)); + cur_addr = edma_readl(fsl_chan->edma, ®s->tcd[ch].daddr); /* figure out the finished and calculate the residue */ for (i = 0; i < fsl_chan->edesc->n_tcds; i++) { @@ -261,7 +259,7 @@ static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan, struct fsl_edma_hw_tcd *tcd) { struct fsl_edma_engine *edma = fsl_chan->edma; - void __iomem *addr = fsl_chan->edma->membase; + struct edma_regs *regs = &fsl_chan->edma->regs; u32 ch = fsl_chan->vchan.chan.chan_id; /* @@ -269,24 +267,24 @@ static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan, * endian format. However, we need to load the TCD registers in * big- or little-endian obeying the eDMA engine model endian. */ - edma_writew(edma, 0, addr + EDMA_TCD_CSR(ch)); - edma_writel(edma, le32_to_cpu(tcd->saddr), addr + EDMA_TCD_SADDR(ch)); - edma_writel(edma, le32_to_cpu(tcd->daddr), addr + EDMA_TCD_DADDR(ch)); + edma_writew(edma, 0, ®s->tcd[ch].csr); + edma_writel(edma, le32_to_cpu(tcd->saddr), ®s->tcd[ch].saddr); + edma_writel(edma, le32_to_cpu(tcd->daddr), ®s->tcd[ch].daddr); - edma_writew(edma, le16_to_cpu(tcd->attr), addr + EDMA_TCD_ATTR(ch)); - edma_writew(edma, le16_to_cpu(tcd->soff), addr + EDMA_TCD_SOFF(ch)); + edma_writew(edma, le16_to_cpu(tcd->attr), ®s->tcd[ch].attr); + edma_writew(edma, le16_to_cpu(tcd->soff), ®s->tcd[ch].soff); - edma_writel(edma, le32_to_cpu(tcd->nbytes), addr + EDMA_TCD_NBYTES(ch)); - edma_writel(edma, le32_to_cpu(tcd->slast), addr + EDMA_TCD_SLAST(ch)); + edma_writel(edma, le32_to_cpu(tcd->nbytes), ®s->tcd[ch].nbytes); + edma_writel(edma, le32_to_cpu(tcd->slast), ®s->tcd[ch].slast); - edma_writew(edma, le16_to_cpu(tcd->citer), addr + EDMA_TCD_CITER(ch)); - edma_writew(edma, le16_to_cpu(tcd->biter), addr + EDMA_TCD_BITER(ch)); - edma_writew(edma, le16_to_cpu(tcd->doff), addr + EDMA_TCD_DOFF(ch)); + edma_writew(edma, le16_to_cpu(tcd->citer), ®s->tcd[ch].citer); + edma_writew(edma, le16_to_cpu(tcd->biter), ®s->tcd[ch].biter); + edma_writew(edma, le16_to_cpu(tcd->doff), ®s->tcd[ch].doff); - edma_writel(edma, - le32_to_cpu(tcd->dlast_sga), addr + EDMA_TCD_DLAST_SGA(ch)); + edma_writel(edma, le32_to_cpu(tcd->dlast_sga), + ®s->tcd[ch].dlast_sga); - edma_writew(edma, le16_to_cpu(tcd->csr), addr + EDMA_TCD_CSR(ch)); + edma_writew(edma, le16_to_cpu(tcd->csr), ®s->tcd[ch].csr); } static inline @@ -308,15 +306,15 @@ void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst, tcd->attr = cpu_to_le16(attr); - tcd->soff = cpu_to_le16(EDMA_TCD_SOFF_SOFF(soff)); + tcd->soff = cpu_to_le16(soff); - tcd->nbytes = cpu_to_le32(EDMA_TCD_NBYTES_NBYTES(nbytes)); - tcd->slast = cpu_to_le32(EDMA_TCD_SLAST_SLAST(slast)); + tcd->nbytes = cpu_to_le32(nbytes); + tcd->slast = cpu_to_le32(slast); tcd->citer = cpu_to_le16(EDMA_TCD_CITER_CITER(citer)); - tcd->doff = cpu_to_le16(EDMA_TCD_DOFF_DOFF(doff)); + tcd->doff = cpu_to_le16(doff); - tcd->dlast_sga = cpu_to_le32(EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga)); + tcd->dlast_sga = cpu_to_le32(dlast_sga); tcd->biter = cpu_to_le16(EDMA_TCD_BITER_BITER(biter)); if (major_int) @@ -549,4 +547,52 @@ void fsl_edma_cleanup_vchan(struct dma_device *dmadev) } EXPORT_SYMBOL_GPL(fsl_edma_cleanup_vchan); +/* + * On the 32 channels Vybrid/mpc577x edma version (here called "v1"), + * register offsets are different compared to ColdFire mcf5441x 64 channels + * edma (here called "v2"). + * + * This function sets up register offsets as per proper declared version + * so must be called in xxx_edma_probe() just after setting the + * edma "version" and "membase" appropriately. + */ +void fsl_edma_setup_regs(struct fsl_edma_engine *edma) +{ + edma->regs.cr = edma->membase + EDMA_CR; + edma->regs.es = edma->membase + EDMA_ES; + edma->regs.erql = edma->membase + EDMA_ERQ; + edma->regs.eeil = edma->membase + EDMA_EEI; + + edma->regs.serq = edma->membase + ((edma->version == v1) ? + EDMA_SERQ : EDMA64_SERQ); + edma->regs.cerq = edma->membase + ((edma->version == v1) ? + EDMA_CERQ : EDMA64_CERQ); + edma->regs.seei = edma->membase + ((edma->version == v1) ? + EDMA_SEEI : EDMA64_SEEI); + edma->regs.ceei = edma->membase + ((edma->version == v1) ? + EDMA_CEEI : EDMA64_CEEI); + edma->regs.cint = edma->membase + ((edma->version == v1) ? + EDMA_CINT : EDMA64_CINT); + edma->regs.cerr = edma->membase + ((edma->version == v1) ? + EDMA_CERR : EDMA64_CERR); + edma->regs.ssrt = edma->membase + ((edma->version == v1) ? + EDMA_SSRT : EDMA64_SSRT); + edma->regs.cdne = edma->membase + ((edma->version == v1) ? + EDMA_CDNE : EDMA64_CDNE); + edma->regs.intl = edma->membase + ((edma->version == v1) ? + EDMA_INTR : EDMA64_INTL); + edma->regs.errl = edma->membase + ((edma->version == v1) ? + EDMA_ERR : EDMA64_ERRL); + + if (edma->version == v2) { + edma->regs.erqh = edma->membase + EDMA64_ERQH; + edma->regs.eeih = edma->membase + EDMA64_EEIH; + edma->regs.errh = edma->membase + EDMA64_ERRH; + edma->regs.inth = edma->membase + EDMA64_INTH; + } + + edma->regs.tcd = edma->membase + EDMA_TCD; +} +EXPORT_SYMBOL_GPL(fsl_edma_setup_regs); + MODULE_LICENSE("GPL v2"); diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h index 5458dd3b6da8..b2ee89ea895a 100644 --- a/drivers/dma/fsl-edma-common.h +++ b/drivers/dma/fsl-edma-common.h @@ -8,35 +8,6 @@ #include "virt-dma.h" -#define EDMA_CR 0x00 -#define EDMA_ES 0x04 -#define EDMA_ERQ 0x0C -#define EDMA_EEI 0x14 -#define EDMA_SERQ 0x1B -#define EDMA_CERQ 0x1A -#define EDMA_SEEI 0x19 -#define EDMA_CEEI 0x18 -#define EDMA_CINT 0x1F -#define EDMA_CERR 0x1E -#define EDMA_SSRT 0x1D -#define EDMA_CDNE 0x1C -#define EDMA_INTR 0x24 -#define EDMA_ERR 0x2C - -#define EDMA_TCD_SADDR(x) (0x1000 + 32 * (x)) -#define EDMA_TCD_SOFF(x) (0x1004 + 32 * (x)) -#define EDMA_TCD_ATTR(x) (0x1006 + 32 * (x)) -#define EDMA_TCD_NBYTES(x) (0x1008 + 32 * (x)) -#define EDMA_TCD_SLAST(x) (0x100C + 32 * (x)) -#define EDMA_TCD_DADDR(x) (0x1010 + 32 * (x)) -#define EDMA_TCD_DOFF(x) (0x1014 + 32 * (x)) -#define EDMA_TCD_CITER_ELINK(x) (0x1016 + 32 * (x)) -#define EDMA_TCD_CITER(x) (0x1016 + 32 * (x)) -#define EDMA_TCD_DLAST_SGA(x) (0x1018 + 32 * (x)) -#define EDMA_TCD_CSR(x) (0x101C + 32 * (x)) -#define EDMA_TCD_BITER_ELINK(x) (0x101E + 32 * (x)) -#define EDMA_TCD_BITER(x) (0x101E + 32 * (x)) - #define EDMA_CR_EDBG BIT(1) #define EDMA_CR_ERCA BIT(2) #define EDMA_CR_ERGA BIT(3) @@ -114,6 +85,31 @@ struct fsl_edma_hw_tcd { __le16 biter; }; +/* + * These are iomem pointers, for both v32 and v64. + */ +struct edma_regs { + void __iomem *cr; + void __iomem *es; + void __iomem *erqh; + void __iomem *erql; /* aka erq on v32 */ + void __iomem *eeih; + void __iomem *eeil; /* aka eei on v32 */ + void __iomem *seei; + void __iomem *ceei; + void __iomem *serq; + void __iomem *cerq; + void __iomem *cint; + void __iomem *cerr; + void __iomem *ssrt; + void __iomem *cdne; + void __iomem *inth; + void __iomem *intl; + void __iomem *errh; + void __iomem *errl; + struct fsl_edma_hw_tcd __iomem *tcd; +}; + struct fsl_edma_sw_tcd { dma_addr_t ptcd; struct fsl_edma_hw_tcd *vtcd; @@ -147,6 +143,11 @@ struct fsl_edma_desc { struct fsl_edma_sw_tcd tcd[]; }; +enum edma_version { + v1, /* 32ch, Vybdir, mpc57x, etc */ + v2, /* 64ch Coldfire */ +}; + struct fsl_edma_engine { struct dma_device dma_dev; void __iomem *membase; @@ -157,6 +158,8 @@ struct fsl_edma_engine { int txirq; int errirq; bool big_endian; + enum edma_version version; + struct edma_regs regs; struct fsl_edma_chan chans[]; }; @@ -237,5 +240,6 @@ void fsl_edma_issue_pending(struct dma_chan *chan); int fsl_edma_alloc_chan_resources(struct dma_chan *chan); void fsl_edma_free_chan_resources(struct dma_chan *chan); void fsl_edma_cleanup_vchan(struct dma_device *dmadev); +void fsl_edma_setup_regs(struct fsl_edma_engine *edma); #endif /* _FSL_EDMA_COMMON_H_ */ diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index 337ad0c0ea78..34d70112fcc9 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -28,19 +28,16 @@ static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id) { struct fsl_edma_engine *fsl_edma = dev_id; unsigned int intr, ch; - void __iomem *base_addr; + struct edma_regs *regs = &fsl_edma->regs; struct fsl_edma_chan *fsl_chan; - base_addr = fsl_edma->membase; - - intr = edma_readl(fsl_edma, base_addr + EDMA_INTR); + intr = edma_readl(fsl_edma, regs->intl); if (!intr) return IRQ_NONE; for (ch = 0; ch < fsl_edma->n_chans; ch++) { if (intr & (0x1 << ch)) { - edma_writeb(fsl_edma, EDMA_CINT_CINT(ch), - base_addr + EDMA_CINT); + edma_writeb(fsl_edma, EDMA_CINT_CINT(ch), regs->cint); fsl_chan = &fsl_edma->chans[ch]; @@ -68,16 +65,16 @@ static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id) { struct fsl_edma_engine *fsl_edma = dev_id; unsigned int err, ch; + struct edma_regs *regs = &fsl_edma->regs; - err = edma_readl(fsl_edma, fsl_edma->membase + EDMA_ERR); + err = edma_readl(fsl_edma, regs->errl); if (!err) return IRQ_NONE; for (ch = 0; ch < fsl_edma->n_chans; ch++) { if (err & (0x1 << ch)) { fsl_edma_disable_request(&fsl_edma->chans[ch]); - edma_writeb(fsl_edma, EDMA_CERR_CERR(ch), - fsl_edma->membase + EDMA_CERR); + edma_writeb(fsl_edma, EDMA_CERR_CERR(ch), regs->cerr); fsl_edma->chans[ch].status = DMA_ERROR; fsl_edma->chans[ch].idle = true; } @@ -192,6 +189,7 @@ static int fsl_edma_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct fsl_edma_engine *fsl_edma; struct fsl_edma_chan *fsl_chan; + struct edma_regs *regs; struct resource *res; int len, chans; int ret, i; @@ -207,6 +205,7 @@ static int fsl_edma_probe(struct platform_device *pdev) if (!fsl_edma) return -ENOMEM; + fsl_edma->version = v1; fsl_edma->n_chans = chans; mutex_init(&fsl_edma->fsl_edma_mutex); @@ -215,6 +214,9 @@ static int fsl_edma_probe(struct platform_device *pdev) if (IS_ERR(fsl_edma->membase)) return PTR_ERR(fsl_edma->membase); + fsl_edma_setup_regs(fsl_edma); + regs = &fsl_edma->regs; + for (i = 0; i < DMAMUX_NR; i++) { char clkname[32]; @@ -255,11 +257,11 @@ static int fsl_edma_probe(struct platform_device *pdev) fsl_chan->vchan.desc_free = fsl_edma_free_desc; vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); - edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i)); + edma_writew(fsl_edma, 0x0, ®s->tcd[i].csr); fsl_edma_chan_mux(fsl_chan, 0, false); } - edma_writel(fsl_edma, ~0, fsl_edma->membase + EDMA_INTR); + edma_writel(fsl_edma, ~0, regs->intl); ret = fsl_edma_irq_init(pdev, fsl_edma); if (ret) return ret; @@ -306,7 +308,7 @@ static int fsl_edma_probe(struct platform_device *pdev) } /* enable round robin arbitration */ - edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, fsl_edma->membase + EDMA_CR); + edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); return 0; } @@ -353,18 +355,18 @@ static int fsl_edma_resume_early(struct device *dev) { struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev); struct fsl_edma_chan *fsl_chan; + struct edma_regs *regs = &fsl_edma->regs; int i; for (i = 0; i < fsl_edma->n_chans; i++) { fsl_chan = &fsl_edma->chans[i]; fsl_chan->pm_state = RUNNING; - edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i)); + edma_writew(fsl_edma, 0x0, ®s->tcd[i].csr); if (fsl_chan->slave_id != 0) fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, true); } - edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, - fsl_edma->membase + EDMA_CR); + edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); return 0; } -- cgit v1.2.3 From 4d6d3a90e4acfdcf6134a854da20a061cb9da2a6 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Sun, 19 Aug 2018 19:27:15 +0200 Subject: dmaengine: fsl-edma: fix macros This patch fixes macros to use BIT() and GENMASK(), removing also some unneeded. Signed-off-by: Angelo Dureghello Tested-by: Krzysztof Kozlowski Signed-off-by: Vinod Koul --- drivers/dma/fsl-edma-common.h | 50 +++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 28 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h index b2ee89ea895a..a6f5b99ee95f 100644 --- a/drivers/dma/fsl-edma-common.h +++ b/drivers/dma/fsl-edma-common.h @@ -18,34 +18,28 @@ #define EDMA_CR_ECX BIT(16) #define EDMA_CR_CX BIT(17) -#define EDMA_SEEI_SEEI(x) ((x) & 0x1F) -#define EDMA_CEEI_CEEI(x) ((x) & 0x1F) -#define EDMA_CINT_CINT(x) ((x) & 0x1F) -#define EDMA_CERR_CERR(x) ((x) & 0x1F) - -#define EDMA_TCD_ATTR_DSIZE(x) (((x) & 0x0007)) -#define EDMA_TCD_ATTR_DMOD(x) (((x) & 0x001F) << 3) -#define EDMA_TCD_ATTR_SSIZE(x) (((x) & 0x0007) << 8) -#define EDMA_TCD_ATTR_SMOD(x) (((x) & 0x001F) << 11) -#define EDMA_TCD_ATTR_SSIZE_8BIT (0x0000) -#define EDMA_TCD_ATTR_SSIZE_16BIT (0x0100) -#define EDMA_TCD_ATTR_SSIZE_32BIT (0x0200) -#define EDMA_TCD_ATTR_SSIZE_64BIT (0x0300) -#define EDMA_TCD_ATTR_SSIZE_32BYTE (0x0500) -#define EDMA_TCD_ATTR_DSIZE_8BIT (0x0000) -#define EDMA_TCD_ATTR_DSIZE_16BIT (0x0001) -#define EDMA_TCD_ATTR_DSIZE_32BIT (0x0002) -#define EDMA_TCD_ATTR_DSIZE_64BIT (0x0003) -#define EDMA_TCD_ATTR_DSIZE_32BYTE (0x0005) - -#define EDMA_TCD_SOFF_SOFF(x) (x) -#define EDMA_TCD_NBYTES_NBYTES(x) (x) -#define EDMA_TCD_SLAST_SLAST(x) (x) -#define EDMA_TCD_DADDR_DADDR(x) (x) -#define EDMA_TCD_CITER_CITER(x) ((x) & 0x7FFF) -#define EDMA_TCD_DOFF_DOFF(x) (x) -#define EDMA_TCD_DLAST_SGA_DLAST_SGA(x) (x) -#define EDMA_TCD_BITER_BITER(x) ((x) & 0x7FFF) +#define EDMA_SEEI_SEEI(x) ((x) & GENMASK(4, 0)) +#define EDMA_CEEI_CEEI(x) ((x) & GENMASK(4, 0)) +#define EDMA_CINT_CINT(x) ((x) & GENMASK(4, 0)) +#define EDMA_CERR_CERR(x) ((x) & GENMASK(4, 0)) + +#define EDMA_TCD_ATTR_DSIZE(x) (((x) & GENMASK(2, 0))) +#define EDMA_TCD_ATTR_DMOD(x) (((x) & GENMASK(4, 0)) << 3) +#define EDMA_TCD_ATTR_SSIZE(x) (((x) & GENMASK(2, 0)) << 8) +#define EDMA_TCD_ATTR_SMOD(x) (((x) & GENMASK(4, 0)) << 11) +#define EDMA_TCD_ATTR_DSIZE_8BIT 0 +#define EDMA_TCD_ATTR_DSIZE_16BIT BIT(0) +#define EDMA_TCD_ATTR_DSIZE_32BIT BIT(1) +#define EDMA_TCD_ATTR_DSIZE_64BIT (BIT(0) | BIT(1)) +#define EDMA_TCD_ATTR_DSIZE_32BYTE (BIT(3) | BIT(0)) +#define EDMA_TCD_ATTR_SSIZE_8BIT 0 +#define EDMA_TCD_ATTR_SSIZE_16BIT (EDMA_TCD_ATTR_DSIZE_16BIT << 8) +#define EDMA_TCD_ATTR_SSIZE_32BIT (EDMA_TCD_ATTR_DSIZE_32BIT << 8) +#define EDMA_TCD_ATTR_SSIZE_64BIT (EDMA_TCD_ATTR_DSIZE_64BIT << 8) +#define EDMA_TCD_ATTR_SSIZE_32BYTE (EDMA_TCD_ATTR_DSIZE_32BYTE << 8) + +#define EDMA_TCD_CITER_CITER(x) ((x) & GENMASK(14, 0)) +#define EDMA_TCD_BITER_BITER(x) ((x) & GENMASK(14, 0)) #define EDMA_TCD_CSR_START BIT(0) #define EDMA_TCD_CSR_INT_MAJOR BIT(1) -- cgit v1.2.3 From e7a3ff92eaf19eab14e8149758428e680c61706b Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Sun, 19 Aug 2018 19:27:16 +0200 Subject: dmaengine: fsl-edma: add ColdFire mcf5441x edma support This patch adds support for ColdFire mcf5441x-family edma module. The ColdFire edma module is slightly different from fsl-edma, so a new driver is added. But most of the code is common between fsl-edma and mcf-edma so it has been collected into a separate common module fsl-edma-common (patch 1/3). Signed-off-by: Angelo Dureghello Tested-by: Krzysztof Kozlowski Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 11 + drivers/dma/Makefile | 1 + drivers/dma/fsl-edma-common.c | 24 ++- drivers/dma/mcf-edma.c | 317 +++++++++++++++++++++++++++++ include/linux/platform_data/dma-mcf-edma.h | 38 ++++ 5 files changed, 387 insertions(+), 4 deletions(-) create mode 100644 drivers/dma/mcf-edma.c create mode 100644 include/linux/platform_data/dma-mcf-edma.h (limited to 'drivers/dma') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index dacf3f42426d..05104325d685 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -321,6 +321,17 @@ config LPC18XX_DMAMUX Enable support for DMA on NXP LPC18xx/43xx platforms with PL080 and multiplexed DMA request lines. +config MCF_EDMA + tristate "Freescale eDMA engine support, ColdFire mcf5441x SoCs" + depends on M5441x || COMPILE_TEST + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Support the Freescale ColdFire eDMA engine, 64-channel + implementation that performs complex data transfers with + minimal intervention from a host processor. + This module can be found on Freescale ColdFire mcf5441x SoCs. + config MMP_PDMA bool "MMP PDMA support" depends on ARCH_MMP || ARCH_PXA || COMPILE_TEST diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 21db4e51b246..7fcc4d8e336d 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_DW_DMAC_CORE) += dw/ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o +obj-$(CONFIG_MCF_EDMA) += mcf-edma.o fsl-edma-common.o obj-$(CONFIG_FSL_RAID) += fsl_raid.o obj-$(CONFIG_HSU_DMA) += hsu/ obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c index 227034de256e..8ba80f4b6f55 100644 --- a/drivers/dma/fsl-edma-common.c +++ b/drivers/dma/fsl-edma-common.c @@ -46,8 +46,16 @@ static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan) struct edma_regs *regs = &fsl_chan->edma->regs; u32 ch = fsl_chan->vchan.chan.chan_id; - edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei); - edma_writeb(fsl_chan->edma, ch, regs->serq); + if (fsl_chan->edma->version == v1) { + edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei); + edma_writeb(fsl_chan->edma, ch, regs->serq); + } else { + /* ColdFire is big endian, and accesses natively + * big endian I/O peripherals + */ + iowrite8(EDMA_SEEI_SEEI(ch), regs->seei); + iowrite8(ch, regs->serq); + } } void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan) @@ -55,8 +63,16 @@ void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan) struct edma_regs *regs = &fsl_chan->edma->regs; u32 ch = fsl_chan->vchan.chan.chan_id; - edma_writeb(fsl_chan->edma, ch, regs->cerq); - edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei); + if (fsl_chan->edma->version == v1) { + edma_writeb(fsl_chan->edma, ch, regs->cerq); + edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei); + } else { + /* ColdFire is big endian, and accesses natively + * big endian I/O peripherals + */ + iowrite8(ch, regs->cerq); + iowrite8(EDMA_CEEI_CEEI(ch), regs->ceei); + } } EXPORT_SYMBOL_GPL(fsl_edma_disable_request); diff --git a/drivers/dma/mcf-edma.c b/drivers/dma/mcf-edma.c new file mode 100644 index 000000000000..4d30d5302649 --- /dev/null +++ b/drivers/dma/mcf-edma.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (c) 2013-2014 Freescale Semiconductor, Inc +// Copyright (c) 2017 Sysam, Angelo Dureghello + +#include +#include +#include +#include +#include + +#include "fsl-edma-common.h" + +#define EDMA_CHANNELS 64 +#define EDMA_MASK_CH(x) ((x) & GENMASK(5, 0)) + +static irqreturn_t mcf_edma_tx_handler(int irq, void *dev_id) +{ + struct fsl_edma_engine *mcf_edma = dev_id; + struct edma_regs *regs = &mcf_edma->regs; + unsigned int ch; + struct fsl_edma_chan *mcf_chan; + u64 intmap; + + intmap = ioread32(regs->inth); + intmap <<= 32; + intmap |= ioread32(regs->intl); + if (!intmap) + return IRQ_NONE; + + for (ch = 0; ch < mcf_edma->n_chans; ch++) { + if (intmap & BIT(ch)) { + iowrite8(EDMA_MASK_CH(ch), regs->cint); + + mcf_chan = &mcf_edma->chans[ch]; + + spin_lock(&mcf_chan->vchan.lock); + if (!mcf_chan->edesc->iscyclic) { + list_del(&mcf_chan->edesc->vdesc.node); + vchan_cookie_complete(&mcf_chan->edesc->vdesc); + mcf_chan->edesc = NULL; + mcf_chan->status = DMA_COMPLETE; + mcf_chan->idle = true; + } else { + vchan_cyclic_callback(&mcf_chan->edesc->vdesc); + } + + if (!mcf_chan->edesc) + fsl_edma_xfer_desc(mcf_chan); + + spin_unlock(&mcf_chan->vchan.lock); + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t mcf_edma_err_handler(int irq, void *dev_id) +{ + struct fsl_edma_engine *mcf_edma = dev_id; + struct edma_regs *regs = &mcf_edma->regs; + unsigned int err, ch; + + err = ioread32(regs->errl); + if (!err) + return IRQ_NONE; + + for (ch = 0; ch < (EDMA_CHANNELS / 2); ch++) { + if (err & BIT(ch)) { + fsl_edma_disable_request(&mcf_edma->chans[ch]); + iowrite8(EDMA_CERR_CERR(ch), regs->cerr); + mcf_edma->chans[ch].status = DMA_ERROR; + mcf_edma->chans[ch].idle = true; + } + } + + err = ioread32(regs->errh); + if (!err) + return IRQ_NONE; + + for (ch = (EDMA_CHANNELS / 2); ch < EDMA_CHANNELS; ch++) { + if (err & (BIT(ch - (EDMA_CHANNELS / 2)))) { + fsl_edma_disable_request(&mcf_edma->chans[ch]); + iowrite8(EDMA_CERR_CERR(ch), regs->cerr); + mcf_edma->chans[ch].status = DMA_ERROR; + mcf_edma->chans[ch].idle = true; + } + } + + return IRQ_HANDLED; +} + +static int mcf_edma_irq_init(struct platform_device *pdev, + struct fsl_edma_engine *mcf_edma) +{ + int ret = 0, i; + struct resource *res; + + res = platform_get_resource_byname(pdev, + IORESOURCE_IRQ, "edma-tx-00-15"); + if (!res) + return -1; + + for (ret = 0, i = res->start; i <= res->end; ++i) + ret |= request_irq(i, mcf_edma_tx_handler, 0, "eDMA", mcf_edma); + if (ret) + return ret; + + res = platform_get_resource_byname(pdev, + IORESOURCE_IRQ, "edma-tx-16-55"); + if (!res) + return -1; + + for (ret = 0, i = res->start; i <= res->end; ++i) + ret |= request_irq(i, mcf_edma_tx_handler, 0, "eDMA", mcf_edma); + if (ret) + return ret; + + ret = platform_get_irq_byname(pdev, "edma-tx-56-63"); + if (ret != -ENXIO) { + ret = request_irq(ret, mcf_edma_tx_handler, + 0, "eDMA", mcf_edma); + if (ret) + return ret; + } + + ret = platform_get_irq_byname(pdev, "edma-err"); + if (ret != -ENXIO) { + ret = request_irq(ret, mcf_edma_err_handler, + 0, "eDMA", mcf_edma); + if (ret) + return ret; + } + + return 0; +} + +static void mcf_edma_irq_free(struct platform_device *pdev, + struct fsl_edma_engine *mcf_edma) +{ + int irq; + struct resource *res; + + res = platform_get_resource_byname(pdev, + IORESOURCE_IRQ, "edma-tx-00-15"); + if (res) { + for (irq = res->start; irq <= res->end; irq++) + free_irq(irq, mcf_edma); + } + + res = platform_get_resource_byname(pdev, + IORESOURCE_IRQ, "edma-tx-16-55"); + if (res) { + for (irq = res->start; irq <= res->end; irq++) + free_irq(irq, mcf_edma); + } + + irq = platform_get_irq_byname(pdev, "edma-tx-56-63"); + if (irq != -ENXIO) + free_irq(irq, mcf_edma); + + irq = platform_get_irq_byname(pdev, "edma-err"); + if (irq != -ENXIO) + free_irq(irq, mcf_edma); +} + +static int mcf_edma_probe(struct platform_device *pdev) +{ + struct mcf_edma_platform_data *pdata; + struct fsl_edma_engine *mcf_edma; + struct fsl_edma_chan *mcf_chan; + struct edma_regs *regs; + struct resource *res; + int ret, i, len, chans; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "no platform data supplied\n"); + return -EINVAL; + } + + chans = pdata->dma_channels; + len = sizeof(*mcf_edma) + sizeof(*mcf_chan) * chans; + mcf_edma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!mcf_edma) + return -ENOMEM; + + mcf_edma->n_chans = chans; + + /* Set up version for ColdFire edma */ + mcf_edma->version = v2; + mcf_edma->big_endian = 1; + + if (!mcf_edma->n_chans) { + dev_info(&pdev->dev, "setting default channel number to 64"); + mcf_edma->n_chans = 64; + } + + mutex_init(&mcf_edma->fsl_edma_mutex); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + mcf_edma->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mcf_edma->membase)) + return PTR_ERR(mcf_edma->membase); + + fsl_edma_setup_regs(mcf_edma); + regs = &mcf_edma->regs; + + INIT_LIST_HEAD(&mcf_edma->dma_dev.channels); + for (i = 0; i < mcf_edma->n_chans; i++) { + struct fsl_edma_chan *mcf_chan = &mcf_edma->chans[i]; + + mcf_chan->edma = mcf_edma; + mcf_chan->slave_id = i; + mcf_chan->idle = true; + mcf_chan->vchan.desc_free = fsl_edma_free_desc; + vchan_init(&mcf_chan->vchan, &mcf_edma->dma_dev); + iowrite32(0x0, ®s->tcd[i].csr); + } + + iowrite32(~0, regs->inth); + iowrite32(~0, regs->intl); + + ret = mcf_edma_irq_init(pdev, mcf_edma); + if (ret) + return ret; + + dma_cap_set(DMA_PRIVATE, mcf_edma->dma_dev.cap_mask); + dma_cap_set(DMA_SLAVE, mcf_edma->dma_dev.cap_mask); + dma_cap_set(DMA_CYCLIC, mcf_edma->dma_dev.cap_mask); + + mcf_edma->dma_dev.dev = &pdev->dev; + mcf_edma->dma_dev.device_alloc_chan_resources = + fsl_edma_alloc_chan_resources; + mcf_edma->dma_dev.device_free_chan_resources = + fsl_edma_free_chan_resources; + mcf_edma->dma_dev.device_config = fsl_edma_slave_config; + mcf_edma->dma_dev.device_prep_dma_cyclic = + fsl_edma_prep_dma_cyclic; + mcf_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg; + mcf_edma->dma_dev.device_tx_status = fsl_edma_tx_status; + mcf_edma->dma_dev.device_pause = fsl_edma_pause; + mcf_edma->dma_dev.device_resume = fsl_edma_resume; + mcf_edma->dma_dev.device_terminate_all = fsl_edma_terminate_all; + mcf_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending; + + mcf_edma->dma_dev.src_addr_widths = FSL_EDMA_BUSWIDTHS; + mcf_edma->dma_dev.dst_addr_widths = FSL_EDMA_BUSWIDTHS; + mcf_edma->dma_dev.directions = + BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + + mcf_edma->dma_dev.filter.fn = mcf_edma_filter_fn; + mcf_edma->dma_dev.filter.map = pdata->slave_map; + mcf_edma->dma_dev.filter.mapcnt = pdata->slavecnt; + + platform_set_drvdata(pdev, mcf_edma); + + ret = dma_async_device_register(&mcf_edma->dma_dev); + if (ret) { + dev_err(&pdev->dev, + "Can't register Freescale eDMA engine. (%d)\n", ret); + return ret; + } + + /* Enable round robin arbitration */ + iowrite32(EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); + + return 0; +} + +static int mcf_edma_remove(struct platform_device *pdev) +{ + struct fsl_edma_engine *mcf_edma = platform_get_drvdata(pdev); + + mcf_edma_irq_free(pdev, mcf_edma); + fsl_edma_cleanup_vchan(&mcf_edma->dma_dev); + dma_async_device_unregister(&mcf_edma->dma_dev); + + return 0; +} + +static struct platform_driver mcf_edma_driver = { + .driver = { + .name = "mcf-edma", + }, + .probe = mcf_edma_probe, + .remove = mcf_edma_remove, +}; + +bool mcf_edma_filter_fn(struct dma_chan *chan, void *param) +{ + if (chan->device->dev->driver == &mcf_edma_driver.driver) { + struct fsl_edma_chan *mcf_chan = to_fsl_edma_chan(chan); + + return (mcf_chan->slave_id == (u32)param); + } + + return false; +} +EXPORT_SYMBOL(mcf_edma_filter_fn); + +static int __init mcf_edma_init(void) +{ + return platform_driver_register(&mcf_edma_driver); +} +subsys_initcall(mcf_edma_init); + +static void __exit mcf_edma_exit(void) +{ + platform_driver_unregister(&mcf_edma_driver); +} +module_exit(mcf_edma_exit); + +MODULE_ALIAS("platform:mcf-edma"); +MODULE_DESCRIPTION("Freescale eDMA engine driver, ColdFire family"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/dma-mcf-edma.h b/include/linux/platform_data/dma-mcf-edma.h new file mode 100644 index 000000000000..d718ccfa3421 --- /dev/null +++ b/include/linux/platform_data/dma-mcf-edma.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Freescale eDMA platform data, ColdFire SoC's family. + * + * Copyright (c) 2017 Angelo Dureghello + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LINUX_PLATFORM_DATA_MCF_EDMA_H__ +#define __LINUX_PLATFORM_DATA_MCF_EDMA_H__ + +struct dma_slave_map; + +bool mcf_edma_filter_fn(struct dma_chan *chan, void *param); + +#define MCF_EDMA_FILTER_PARAM(ch) ((void *)ch) + +/** + * struct mcf_edma_platform_data - platform specific data for eDMA engine + * + * @ver The eDMA module version. + * @dma_channels The number of eDMA channels. + */ +struct mcf_edma_platform_data { + int dma_channels; + const struct dma_slave_map *slave_map; + int slavecnt; +}; + +#endif /* __LINUX_PLATFORM_DATA_MCF_EDMA_H__ */ -- cgit v1.2.3 From 54f919a04cf221bc1601d1193682d4379dacacbd Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 29 Aug 2018 23:32:44 +0200 Subject: dmaengine: dma-jz4780: Return error if not probed from DT The driver calls clk_get() with the clock name set to NULL, which means that the driver could only work when probed from devicetree. From now on, we explicitly require the driver to be probed from devicetree. Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index 85820a2d69d4..987899610b46 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -761,6 +761,11 @@ static int jz4780_dma_probe(struct platform_device *pdev) struct resource *res; int i, ret; + if (!dev->of_node) { + dev_err(dev, "This driver must be probed from devicetree\n"); + return -EINVAL; + } + jzdma = devm_kzalloc(dev, sizeof(*jzdma), GFP_KERNEL); if (!jzdma) return -ENOMEM; -- cgit v1.2.3 From 6147b032e544105cafaee25b416f7e919c3bb7d3 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 29 Aug 2018 23:32:45 +0200 Subject: dmaengine: dma-jz4780: Avoid hardcoding number of channels As part of the work to support various other Ingenic JZ47xx SoC versions, which don't feature the same number of DMA channels per core, we now deduce the number of DMA channels available from the devicetree compatible string. Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index 987899610b46..9ec22cf15a33 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -23,8 +24,6 @@ #include "dmaengine.h" #include "virt-dma.h" -#define JZ_DMA_NR_CHANNELS 32 - /* Global registers. */ #define JZ_DMA_REG_DMAC 0x1000 #define JZ_DMA_REG_DIRQP 0x1004 @@ -135,14 +134,19 @@ struct jz4780_dma_chan { unsigned int curr_hwdesc; }; +struct jz4780_dma_soc_data { + unsigned int nb_channels; +}; + struct jz4780_dma_dev { struct dma_device dma_device; void __iomem *base; struct clk *clk; unsigned int irq; + const struct jz4780_dma_soc_data *soc_data; uint32_t chan_reserved; - struct jz4780_dma_chan chan[JZ_DMA_NR_CHANNELS]; + struct jz4780_dma_chan chan[]; }; struct jz4780_dma_filter_data { @@ -648,7 +652,7 @@ static irqreturn_t jz4780_dma_irq_handler(int irq, void *data) pending = jz4780_dma_readl(jzdma, JZ_DMA_REG_DIRQP); - for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) { + for (i = 0; i < jzdma->soc_data->nb_channels; i++) { if (!(pending & (1<args[1]; if (data.channel > -1) { - if (data.channel >= JZ_DMA_NR_CHANNELS) { + if (data.channel >= jzdma->soc_data->nb_channels) { dev_err(jzdma->dma_device.dev, "device requested non-existent channel %u\n", data.channel); @@ -755,6 +759,7 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec, static int jz4780_dma_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + const struct jz4780_dma_soc_data *soc_data; struct jz4780_dma_dev *jzdma; struct jz4780_dma_chan *jzchan; struct dma_device *dd; @@ -766,10 +771,17 @@ static int jz4780_dma_probe(struct platform_device *pdev) return -EINVAL; } - jzdma = devm_kzalloc(dev, sizeof(*jzdma), GFP_KERNEL); + soc_data = device_get_match_data(dev); + if (!soc_data) + return -EINVAL; + + jzdma = devm_kzalloc(dev, sizeof(*jzdma) + + sizeof(*jzdma->chan) * soc_data->nb_channels, + GFP_KERNEL); if (!jzdma) return -ENOMEM; + jzdma->soc_data = soc_data; platform_set_drvdata(pdev, jzdma); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -844,7 +856,7 @@ static int jz4780_dma_probe(struct platform_device *pdev) INIT_LIST_HEAD(&dd->channels); - for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) { + for (i = 0; i < soc_data->nb_channels; i++) { jzchan = &jzdma->chan[i]; jzchan->id = i; @@ -889,15 +901,19 @@ static int jz4780_dma_remove(struct platform_device *pdev) free_irq(jzdma->irq, jzdma); - for (i = 0; i < JZ_DMA_NR_CHANNELS; i++) + for (i = 0; i < jzdma->soc_data->nb_channels; i++) tasklet_kill(&jzdma->chan[i].vchan.task); dma_async_device_unregister(&jzdma->dma_device); return 0; } +static const struct jz4780_dma_soc_data jz4780_dma_soc_data = { + .nb_channels = 32, +}; + static const struct of_device_id jz4780_dma_dt_match[] = { - { .compatible = "ingenic,jz4780-dma", .data = NULL }, + { .compatible = "ingenic,jz4780-dma", .data = &jz4780_dma_soc_data }, {}, }; MODULE_DEVICE_TABLE(of, jz4780_dma_dt_match); -- cgit v1.2.3 From 33633583a23aff572ff6b46a6bc57808b9c9872e Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 29 Aug 2018 23:32:46 +0200 Subject: dmaengine: dma-jz4780: Separate chan/ctrl registers The register area of the JZ4780 DMA core can be split into different sections for different purposes: * one set of registers is used to perform actions at the DMA core level, that will generally affect all channels; * one set of registers per DMA channel, to perform actions at the DMA channel level, that will only affect the channel in question. The problem rises when trying to support new versions of the JZ47xx Ingenic SoC. For instance, the JZ4770 has two DMA cores, each one with six DMA channels, and the register sets are interleaved: By using one memory resource for the channel-specific registers and one memory resource for the core-specific registers, we can support the JZ4770, by initializing the driver once per DMA core with different addresses. Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 112 ++++++++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 41 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index 9ec22cf15a33..adf4d5efced6 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -25,26 +25,26 @@ #include "virt-dma.h" /* Global registers. */ -#define JZ_DMA_REG_DMAC 0x1000 -#define JZ_DMA_REG_DIRQP 0x1004 -#define JZ_DMA_REG_DDR 0x1008 -#define JZ_DMA_REG_DDRS 0x100c -#define JZ_DMA_REG_DMACP 0x101c -#define JZ_DMA_REG_DSIRQP 0x1020 -#define JZ_DMA_REG_DSIRQM 0x1024 -#define JZ_DMA_REG_DCIRQP 0x1028 -#define JZ_DMA_REG_DCIRQM 0x102c +#define JZ_DMA_REG_DMAC 0x00 +#define JZ_DMA_REG_DIRQP 0x04 +#define JZ_DMA_REG_DDR 0x08 +#define JZ_DMA_REG_DDRS 0x0c +#define JZ_DMA_REG_DMACP 0x1c +#define JZ_DMA_REG_DSIRQP 0x20 +#define JZ_DMA_REG_DSIRQM 0x24 +#define JZ_DMA_REG_DCIRQP 0x28 +#define JZ_DMA_REG_DCIRQM 0x2c /* Per-channel registers. */ #define JZ_DMA_REG_CHAN(n) (n * 0x20) -#define JZ_DMA_REG_DSA(n) (0x00 + JZ_DMA_REG_CHAN(n)) -#define JZ_DMA_REG_DTA(n) (0x04 + JZ_DMA_REG_CHAN(n)) -#define JZ_DMA_REG_DTC(n) (0x08 + JZ_DMA_REG_CHAN(n)) -#define JZ_DMA_REG_DRT(n) (0x0c + JZ_DMA_REG_CHAN(n)) -#define JZ_DMA_REG_DCS(n) (0x10 + JZ_DMA_REG_CHAN(n)) -#define JZ_DMA_REG_DCM(n) (0x14 + JZ_DMA_REG_CHAN(n)) -#define JZ_DMA_REG_DDA(n) (0x18 + JZ_DMA_REG_CHAN(n)) -#define JZ_DMA_REG_DSD(n) (0x1c + JZ_DMA_REG_CHAN(n)) +#define JZ_DMA_REG_DSA 0x00 +#define JZ_DMA_REG_DTA 0x04 +#define JZ_DMA_REG_DTC 0x08 +#define JZ_DMA_REG_DRT 0x0c +#define JZ_DMA_REG_DCS 0x10 +#define JZ_DMA_REG_DCM 0x14 +#define JZ_DMA_REG_DDA 0x18 +#define JZ_DMA_REG_DSD 0x1c #define JZ_DMA_DMAC_DMAE BIT(0) #define JZ_DMA_DMAC_AR BIT(2) @@ -85,6 +85,8 @@ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) +#define JZ4780_DMA_CTRL_OFFSET 0x1000 + /** * struct jz4780_dma_hwdesc - descriptor structure read by the DMA controller. * @dcm: value for the DCM (channel command) register @@ -140,7 +142,8 @@ struct jz4780_dma_soc_data { struct jz4780_dma_dev { struct dma_device dma_device; - void __iomem *base; + void __iomem *chn_base; + void __iomem *ctrl_base; struct clk *clk; unsigned int irq; const struct jz4780_dma_soc_data *soc_data; @@ -173,16 +176,28 @@ static inline struct jz4780_dma_dev *jz4780_dma_chan_parent( dma_device); } -static inline uint32_t jz4780_dma_readl(struct jz4780_dma_dev *jzdma, +static inline uint32_t jz4780_dma_chn_readl(struct jz4780_dma_dev *jzdma, + unsigned int chn, unsigned int reg) +{ + return readl(jzdma->chn_base + reg + JZ_DMA_REG_CHAN(chn)); +} + +static inline void jz4780_dma_chn_writel(struct jz4780_dma_dev *jzdma, + unsigned int chn, unsigned int reg, uint32_t val) +{ + writel(val, jzdma->chn_base + reg + JZ_DMA_REG_CHAN(chn)); +} + +static inline uint32_t jz4780_dma_ctrl_readl(struct jz4780_dma_dev *jzdma, unsigned int reg) { - return readl(jzdma->base + reg); + return readl(jzdma->ctrl_base + reg); } -static inline void jz4780_dma_writel(struct jz4780_dma_dev *jzdma, +static inline void jz4780_dma_ctrl_writel(struct jz4780_dma_dev *jzdma, unsigned int reg, uint32_t val) { - writel(val, jzdma->base + reg); + writel(val, jzdma->ctrl_base + reg); } static struct jz4780_dma_desc *jz4780_dma_desc_alloc( @@ -477,17 +492,18 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan) } /* Use 8-word descriptors. */ - jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), JZ_DMA_DCS_DES8); + jz4780_dma_chn_writel(jzdma, jzchan->id, + JZ_DMA_REG_DCS, JZ_DMA_DCS_DES8); /* Write descriptor address and initiate descriptor fetch. */ desc_phys = jzchan->desc->desc_phys + (jzchan->curr_hwdesc * sizeof(*jzchan->desc->desc)); - jz4780_dma_writel(jzdma, JZ_DMA_REG_DDA(jzchan->id), desc_phys); - jz4780_dma_writel(jzdma, JZ_DMA_REG_DDRS, BIT(jzchan->id)); + jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DDA, desc_phys); + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DDRS, BIT(jzchan->id)); /* Enable the channel. */ - jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), - JZ_DMA_DCS_DES8 | JZ_DMA_DCS_CTE); + jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, + JZ_DMA_DCS_DES8 | JZ_DMA_DCS_CTE); } static void jz4780_dma_issue_pending(struct dma_chan *chan) @@ -513,7 +529,7 @@ static int jz4780_dma_terminate_all(struct dma_chan *chan) spin_lock_irqsave(&jzchan->vchan.lock, flags); /* Clear the DMA status and stop the transfer. */ - jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), 0); + jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0); if (jzchan->desc) { vchan_terminate_vdesc(&jzchan->desc->vdesc); jzchan->desc = NULL; @@ -562,8 +578,8 @@ static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan, residue += desc->desc[i].dtc << jzchan->transfer_shift; if (next_sg != 0) { - count = jz4780_dma_readl(jzdma, - JZ_DMA_REG_DTC(jzchan->id)); + count = jz4780_dma_chn_readl(jzdma, jzchan->id, + JZ_DMA_REG_DTC); residue += count << jzchan->transfer_shift; } @@ -610,8 +626,8 @@ static void jz4780_dma_chan_irq(struct jz4780_dma_dev *jzdma, spin_lock(&jzchan->vchan.lock); - dcs = jz4780_dma_readl(jzdma, JZ_DMA_REG_DCS(jzchan->id)); - jz4780_dma_writel(jzdma, JZ_DMA_REG_DCS(jzchan->id), 0); + dcs = jz4780_dma_chn_readl(jzdma, jzchan->id, JZ_DMA_REG_DCS); + jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0); if (dcs & JZ_DMA_DCS_AR) { dev_warn(&jzchan->vchan.chan.dev->device, @@ -650,7 +666,7 @@ static irqreturn_t jz4780_dma_irq_handler(int irq, void *data) uint32_t pending, dmac; int i; - pending = jz4780_dma_readl(jzdma, JZ_DMA_REG_DIRQP); + pending = jz4780_dma_ctrl_readl(jzdma, JZ_DMA_REG_DIRQP); for (i = 0; i < jzdma->soc_data->nb_channels; i++) { if (!(pending & (1<base = devm_ioremap_resource(dev, res); - if (IS_ERR(jzdma->base)) - return PTR_ERR(jzdma->base); + jzdma->chn_base = devm_ioremap_resource(dev, res); + if (IS_ERR(jzdma->chn_base)) + return PTR_ERR(jzdma->chn_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + jzdma->ctrl_base = devm_ioremap_resource(dev, res); + if (IS_ERR(jzdma->ctrl_base)) + return PTR_ERR(jzdma->ctrl_base); + } else { + /* + * On JZ4780, if the second memory resource was not supplied, + * assume we're using an old devicetree, and calculate the + * offset to the control registers. + */ + jzdma->ctrl_base = jzdma->chn_base + JZ4780_DMA_CTRL_OFFSET; + } ret = platform_get_irq(pdev, 0); if (ret < 0) { @@ -850,9 +880,9 @@ static int jz4780_dma_probe(struct platform_device *pdev) * Also set the FMSC bit - it increases MSC performance, so it makes * little sense not to enable it. */ - jz4780_dma_writel(jzdma, JZ_DMA_REG_DMAC, + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC, JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC); - jz4780_dma_writel(jzdma, JZ_DMA_REG_DMACP, 0); + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0); INIT_LIST_HEAD(&dd->channels); -- cgit v1.2.3 From 5eed7d84bcc0fac063e7c4cbd3ffc9990268049d Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 29 Aug 2018 23:32:47 +0200 Subject: dmaengine: dma-jz4780: Use 4-word descriptors The only information we use in the 8-word version of the hardware DMA descriptor that is not present in the 4-word version is the transfer type, aka. the ID of the source or recipient device. Since the transfer type will never change for a DMA channel in use, we can just set it once for all in the corresponding DMA register before starting any transfer. This has several benefits: * the driver will handle twice as many hardware DMA descriptors; * the driver is closer to support the JZ4740, which only supports 4-word hardware DMA descriptors; * the JZ4770 SoC needs the transfer type to be set in the corresponding DMA register anyway, even if 8-word descriptors are in use. Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Reviewed-by: PrasannaKumar Muralidharan Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index adf4d5efced6..7683de9fb9ee 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -95,17 +95,12 @@ * @dtc: transfer count (number of blocks of the transfer size specified in DCM * to transfer) in the low 24 bits, offset of the next descriptor from the * descriptor base address in the upper 8 bits. - * @sd: target/source stride difference (in stride transfer mode). - * @drt: request type */ struct jz4780_dma_hwdesc { uint32_t dcm; uint32_t dsa; uint32_t dta; uint32_t dtc; - uint32_t sd; - uint32_t drt; - uint32_t reserved[2]; }; /* Size of allocations for hardware descriptor blocks. */ @@ -281,7 +276,6 @@ static int jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan, desc->dcm = JZ_DMA_DCM_SAI; desc->dsa = addr; desc->dta = config->dst_addr; - desc->drt = jzchan->transfer_type; width = config->dst_addr_width; maxburst = config->dst_maxburst; @@ -289,7 +283,6 @@ static int jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan, desc->dcm = JZ_DMA_DCM_DAI; desc->dsa = config->src_addr; desc->dta = addr; - desc->drt = jzchan->transfer_type; width = config->src_addr_width; maxburst = config->src_maxburst; @@ -434,9 +427,10 @@ static struct dma_async_tx_descriptor *jz4780_dma_prep_dma_memcpy( tsz = jz4780_dma_transfer_size(dest | src | len, &jzchan->transfer_shift); + jzchan->transfer_type = JZ_DMA_DRT_AUTO; + desc->desc[0].dsa = src; desc->desc[0].dta = dest; - desc->desc[0].drt = JZ_DMA_DRT_AUTO; desc->desc[0].dcm = JZ_DMA_DCM_TIE | JZ_DMA_DCM_SAI | JZ_DMA_DCM_DAI | tsz << JZ_DMA_DCM_TSZ_SHIFT | JZ_DMA_WIDTH_32_BIT << JZ_DMA_DCM_SP_SHIFT | @@ -491,9 +485,12 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan) (jzchan->curr_hwdesc + 1) % jzchan->desc->count; } - /* Use 8-word descriptors. */ - jz4780_dma_chn_writel(jzdma, jzchan->id, - JZ_DMA_REG_DCS, JZ_DMA_DCS_DES8); + /* Use 4-word descriptors. */ + jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0); + + /* Set transfer type. */ + jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DRT, + jzchan->transfer_type); /* Write descriptor address and initiate descriptor fetch. */ desc_phys = jzchan->desc->desc_phys + @@ -503,7 +500,7 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan) /* Enable the channel. */ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, - JZ_DMA_DCS_DES8 | JZ_DMA_DCS_CTE); + JZ_DMA_DCS_CTE); } static void jz4780_dma_issue_pending(struct dma_chan *chan) -- cgit v1.2.3 From c558ecd21c852c97ff98dc6c61f715ba420ec251 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 29 Aug 2018 23:32:48 +0200 Subject: dmaengine: dma-jz4780: Don't depend on MACH_JZ4780 If we make this driver depend on MACH_JZ4780, that means it can be enabled only if we're building a kernel specially crafted for a JZ4780-based board, while most GNU/Linux distributions will want one generic MIPS kernel that works on multiple boards. Signed-off-by: Paul Cercueil Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index dacf3f42426d..a4f95574eb9a 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -143,7 +143,7 @@ config DMA_JZ4740 config DMA_JZ4780 tristate "JZ4780 DMA support" - depends on MACH_JZ4780 || COMPILE_TEST + depends on MIPS || COMPILE_TEST select DMA_ENGINE select DMA_VIRTUAL_CHANNELS help -- cgit v1.2.3 From 29870eb759abf2d3a5fdf89c97906b49cd4521d2 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 29 Aug 2018 23:32:49 +0200 Subject: dmaengine: dma-jz4780: Add support for the JZ4770 SoC The JZ4770 SoC has two DMA cores, each one featuring six DMA channels. The major change is that each channel's clock can be enabled or disabled through register writes. Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 61 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 7 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index 7683de9fb9ee..184d1a2bf9ba 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -29,6 +29,9 @@ #define JZ_DMA_REG_DIRQP 0x04 #define JZ_DMA_REG_DDR 0x08 #define JZ_DMA_REG_DDRS 0x0c +#define JZ_DMA_REG_DCKE 0x10 +#define JZ_DMA_REG_DCKES 0x14 +#define JZ_DMA_REG_DCKEC 0x18 #define JZ_DMA_REG_DMACP 0x1c #define JZ_DMA_REG_DSIRQP 0x20 #define JZ_DMA_REG_DSIRQM 0x24 @@ -87,6 +90,11 @@ #define JZ4780_DMA_CTRL_OFFSET 0x1000 +/* macros for use with jz4780_dma_soc_data.flags */ +#define JZ_SOC_DATA_ALLOW_LEGACY_DT BIT(0) +#define JZ_SOC_DATA_PROGRAMMABLE_DMA BIT(1) +#define JZ_SOC_DATA_PER_CHAN_PM BIT(2) + /** * struct jz4780_dma_hwdesc - descriptor structure read by the DMA controller. * @dcm: value for the DCM (channel command) register @@ -133,6 +141,8 @@ struct jz4780_dma_chan { struct jz4780_dma_soc_data { unsigned int nb_channels; + unsigned int transfer_ord_max; + unsigned long flags; }; struct jz4780_dma_dev { @@ -195,6 +205,20 @@ static inline void jz4780_dma_ctrl_writel(struct jz4780_dma_dev *jzdma, writel(val, jzdma->ctrl_base + reg); } +static inline void jz4780_dma_chan_enable(struct jz4780_dma_dev *jzdma, + unsigned int chn) +{ + if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM) + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKES, BIT(chn)); +} + +static inline void jz4780_dma_chan_disable(struct jz4780_dma_dev *jzdma, + unsigned int chn) +{ + if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM) + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKEC, BIT(chn)); +} + static struct jz4780_dma_desc *jz4780_dma_desc_alloc( struct jz4780_dma_chan *jzchan, unsigned int count, enum dma_transaction_type type) @@ -229,8 +253,10 @@ static void jz4780_dma_desc_free(struct virt_dma_desc *vdesc) kfree(desc); } -static uint32_t jz4780_dma_transfer_size(unsigned long val, uint32_t *shift) +static uint32_t jz4780_dma_transfer_size(struct jz4780_dma_chan *jzchan, + unsigned long val, uint32_t *shift) { + struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); int ord = ffs(val) - 1; /* @@ -242,8 +268,8 @@ static uint32_t jz4780_dma_transfer_size(unsigned long val, uint32_t *shift) */ if (ord == 3) ord = 2; - else if (ord > 7) - ord = 7; + else if (ord > jzdma->soc_data->transfer_ord_max) + ord = jzdma->soc_data->transfer_ord_max; *shift = ord; @@ -295,7 +321,7 @@ static int jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan, * divisible by the transfer size, and we must not use more than the * maximum burst specified by the user. */ - tsz = jz4780_dma_transfer_size(addr | len | (width * maxburst), + tsz = jz4780_dma_transfer_size(jzchan, addr | len | (width * maxburst), &jzchan->transfer_shift); switch (width) { @@ -424,7 +450,7 @@ static struct dma_async_tx_descriptor *jz4780_dma_prep_dma_memcpy( if (!desc) return NULL; - tsz = jz4780_dma_transfer_size(dest | src | len, + tsz = jz4780_dma_transfer_size(jzchan, dest | src | len, &jzchan->transfer_shift); jzchan->transfer_type = JZ_DMA_DRT_AUTO; @@ -485,6 +511,9 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan) (jzchan->curr_hwdesc + 1) % jzchan->desc->count; } + /* Enable the channel's clock. */ + jz4780_dma_chan_enable(jzdma, jzchan->id); + /* Use 4-word descriptors. */ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0); @@ -532,6 +561,8 @@ static int jz4780_dma_terminate_all(struct dma_chan *chan) jzchan->desc = NULL; } + jz4780_dma_chan_disable(jzdma, jzchan->id); + vchan_get_all_descriptors(&jzchan->vchan, &head); spin_unlock_irqrestore(&jzchan->vchan.lock, flags); @@ -543,8 +574,10 @@ static int jz4780_dma_terminate_all(struct dma_chan *chan) static void jz4780_dma_synchronize(struct dma_chan *chan) { struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); + struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); vchan_synchronize(&jzchan->vchan); + jz4780_dma_chan_disable(jzdma, jzchan->id); } static int jz4780_dma_config(struct dma_chan *chan, @@ -812,13 +845,16 @@ static int jz4780_dma_probe(struct platform_device *pdev) jzdma->ctrl_base = devm_ioremap_resource(dev, res); if (IS_ERR(jzdma->ctrl_base)) return PTR_ERR(jzdma->ctrl_base); - } else { + } else if (soc_data->flags & JZ_SOC_DATA_ALLOW_LEGACY_DT) { /* * On JZ4780, if the second memory resource was not supplied, * assume we're using an old devicetree, and calculate the * offset to the control registers. */ jzdma->ctrl_base = jzdma->chn_base + JZ4780_DMA_CTRL_OFFSET; + } else { + dev_err(dev, "failed to get I/O memory\n"); + return -EINVAL; } ret = platform_get_irq(pdev, 0); @@ -879,7 +915,9 @@ static int jz4780_dma_probe(struct platform_device *pdev) */ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC, JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC); - jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0); + + if (soc_data->flags & JZ_SOC_DATA_PROGRAMMABLE_DMA) + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0); INIT_LIST_HEAD(&dd->channels); @@ -935,11 +973,20 @@ static int jz4780_dma_remove(struct platform_device *pdev) return 0; } +static const struct jz4780_dma_soc_data jz4770_dma_soc_data = { + .nb_channels = 6, + .transfer_ord_max = 6, + .flags = JZ_SOC_DATA_PER_CHAN_PM, +}; + static const struct jz4780_dma_soc_data jz4780_dma_soc_data = { .nb_channels = 32, + .transfer_ord_max = 7, + .flags = JZ_SOC_DATA_ALLOW_LEGACY_DT | JZ_SOC_DATA_PROGRAMMABLE_DMA, }; static const struct of_device_id jz4780_dma_dt_match[] = { + { .compatible = "ingenic,jz4770-dma", .data = &jz4770_dma_soc_data }, { .compatible = "ingenic,jz4780-dma", .data = &jz4780_dma_soc_data }, {}, }; -- cgit v1.2.3 From ffaaa8cc4ee644504113c2a480709c59575e2854 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 29 Aug 2018 23:32:50 +0200 Subject: dmaengine: dma-jz4780: Add support for the JZ4740 SoC The JZ4740 SoC has a single DMA core starring six DMA channels. Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Reviewed-by: PrasannaKumar Muralidharan Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index 184d1a2bf9ba..2d194dfa697e 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -973,6 +973,11 @@ static int jz4780_dma_remove(struct platform_device *pdev) return 0; } +static const struct jz4780_dma_soc_data jz4740_dma_soc_data = { + .nb_channels = 6, + .transfer_ord_max = 5, +}; + static const struct jz4780_dma_soc_data jz4770_dma_soc_data = { .nb_channels = 6, .transfer_ord_max = 6, @@ -986,6 +991,7 @@ static const struct jz4780_dma_soc_data jz4780_dma_soc_data = { }; static const struct of_device_id jz4780_dma_dt_match[] = { + { .compatible = "ingenic,jz4740-dma", .data = &jz4740_dma_soc_data }, { .compatible = "ingenic,jz4770-dma", .data = &jz4770_dma_soc_data }, { .compatible = "ingenic,jz4780-dma", .data = &jz4780_dma_soc_data }, {}, -- cgit v1.2.3 From ae9156b63d94bb7e4f8be25e0d67326fbd07e0ef Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 29 Aug 2018 23:32:51 +0200 Subject: dmaengine: dma-jz4780: Add support for the JZ4725B SoC The JZ4725B has one DMA core starring six DMA channels. As for the JZ4770, each DMA channel's clock can be enabled with a register write, the difference here being that once started, it is not possible to turn it off. Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Reviewed-by: PrasannaKumar Muralidharan Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index 2d194dfa697e..565971c2a33c 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -94,6 +94,7 @@ #define JZ_SOC_DATA_ALLOW_LEGACY_DT BIT(0) #define JZ_SOC_DATA_PROGRAMMABLE_DMA BIT(1) #define JZ_SOC_DATA_PER_CHAN_PM BIT(2) +#define JZ_SOC_DATA_NO_DCKES_DCKEC BIT(3) /** * struct jz4780_dma_hwdesc - descriptor structure read by the DMA controller. @@ -208,14 +209,23 @@ static inline void jz4780_dma_ctrl_writel(struct jz4780_dma_dev *jzdma, static inline void jz4780_dma_chan_enable(struct jz4780_dma_dev *jzdma, unsigned int chn) { - if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM) - jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKES, BIT(chn)); + if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM) { + unsigned int reg; + + if (jzdma->soc_data->flags & JZ_SOC_DATA_NO_DCKES_DCKEC) + reg = JZ_DMA_REG_DCKE; + else + reg = JZ_DMA_REG_DCKES; + + jz4780_dma_ctrl_writel(jzdma, reg, BIT(chn)); + } } static inline void jz4780_dma_chan_disable(struct jz4780_dma_dev *jzdma, unsigned int chn) { - if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM) + if ((jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM) && + !(jzdma->soc_data->flags & JZ_SOC_DATA_NO_DCKES_DCKEC)) jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKEC, BIT(chn)); } @@ -978,6 +988,12 @@ static const struct jz4780_dma_soc_data jz4740_dma_soc_data = { .transfer_ord_max = 5, }; +static const struct jz4780_dma_soc_data jz4725b_dma_soc_data = { + .nb_channels = 6, + .transfer_ord_max = 5, + .flags = JZ_SOC_DATA_PER_CHAN_PM | JZ_SOC_DATA_NO_DCKES_DCKEC, +}; + static const struct jz4780_dma_soc_data jz4770_dma_soc_data = { .nb_channels = 6, .transfer_ord_max = 6, @@ -992,6 +1008,7 @@ static const struct jz4780_dma_soc_data jz4780_dma_soc_data = { static const struct of_device_id jz4780_dma_dt_match[] = { { .compatible = "ingenic,jz4740-dma", .data = &jz4740_dma_soc_data }, + { .compatible = "ingenic,jz4725b-dma", .data = &jz4725b_dma_soc_data }, { .compatible = "ingenic,jz4770-dma", .data = &jz4770_dma_soc_data }, { .compatible = "ingenic,jz4780-dma", .data = &jz4780_dma_soc_data }, {}, -- cgit v1.2.3 From 17a8e30e9daf7f65e06a899d37a043c081334e5d Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 29 Aug 2018 23:32:52 +0200 Subject: dmaengine: dma-jz4780: Enable Fast DMA to the AIC With the fast DMA bit set, the DMA will transfer twice as much data per clock period to the AIC, so there is little point not to set it. Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Reviewed-by: PrasannaKumar Muralidharan Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index 565971c2a33c..3a4d0a4b550d 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -52,6 +52,7 @@ #define JZ_DMA_DMAC_DMAE BIT(0) #define JZ_DMA_DMAC_AR BIT(2) #define JZ_DMA_DMAC_HLT BIT(3) +#define JZ_DMA_DMAC_FAIC BIT(27) #define JZ_DMA_DMAC_FMSC BIT(31) #define JZ_DMA_DRT_AUTO 0x8 @@ -923,8 +924,8 @@ static int jz4780_dma_probe(struct platform_device *pdev) * Also set the FMSC bit - it increases MSC performance, so it makes * little sense not to enable it. */ - jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC, - JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC); + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC, JZ_DMA_DMAC_DMAE | + JZ_DMA_DMAC_FAIC | JZ_DMA_DMAC_FMSC); if (soc_data->flags & JZ_SOC_DATA_PROGRAMMABLE_DMA) jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0); -- cgit v1.2.3 From 5a6187d70f7fc0b55db49af4b2a0f35f34efd770 Mon Sep 17 00:00:00 2001 From: Daniel Silsby Date: Wed, 29 Aug 2018 23:32:53 +0200 Subject: dmaengine: dma-jz4780: Add missing residue DTC mask The 'dtc' word in jz DMA descriptors contains two fields: The lowest 24 bits are the transfer count, and upper 8 bits are the DOA offset to next descriptor. The upper 8 bits are now correctly masked off when computing residue in jz4780_dma_desc_residue(). Note that reads of the DTCn hardware reg are automatically masked this way. Signed-off-by: Daniel Silsby Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index 3a4d0a4b550d..bd3cecb800c5 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -616,7 +616,8 @@ static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan, residue = 0; for (i = next_sg; i < desc->count; i++) - residue += desc->desc[i].dtc << jzchan->transfer_shift; + residue += (desc->desc[i].dtc & GENMASK(23, 0)) << + jzchan->transfer_shift; if (next_sg != 0) { count = jz4780_dma_chn_readl(jzdma, jzchan->id, -- cgit v1.2.3 From f3c045dffe738fb2de534a8156f540dff42c315e Mon Sep 17 00:00:00 2001 From: Daniel Silsby Date: Wed, 29 Aug 2018 23:32:54 +0200 Subject: dmaengine: dma-jz4780: Simplify jz4780_dma_desc_residue() Simple cleanup, no changes to actual logic here. Signed-off-by: Daniel Silsby Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index bd3cecb800c5..d055602a92ca 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -610,22 +610,17 @@ static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan, struct jz4780_dma_desc *desc, unsigned int next_sg) { struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); - unsigned int residue, count; + unsigned int count = 0; unsigned int i; - residue = 0; - for (i = next_sg; i < desc->count; i++) - residue += (desc->desc[i].dtc & GENMASK(23, 0)) << - jzchan->transfer_shift; + count += desc->desc[i].dtc & GENMASK(23, 0); - if (next_sg != 0) { - count = jz4780_dma_chn_readl(jzdma, jzchan->id, + if (next_sg != 0) + count += jz4780_dma_chn_readl(jzdma, jzchan->id, JZ_DMA_REG_DTC); - residue += count << jzchan->transfer_shift; - } - return residue; + return count << jzchan->transfer_shift; } static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan, -- cgit v1.2.3 From 9e4e3a4c00c0af333d1c32925b80d12dc571778b Mon Sep 17 00:00:00 2001 From: Daniel Silsby Date: Wed, 29 Aug 2018 23:32:55 +0200 Subject: dmaengine: dma-jz4780: Set DTCn register explicitly Normally, we wouldn't set the channel transfer count register directly when using descriptor-driven transfers. However, there is no harm in doing so, and it allows jz4780_dma_desc_residue() to report the correct residue of an ongoing transfer, no matter when it is called. Signed-off-by: Daniel Silsby Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index d055602a92ca..d3b915ec8a09 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -532,6 +532,15 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan) jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DRT, jzchan->transfer_type); + /* + * Set the transfer count. This is redundant for a descriptor-driven + * transfer. However, there can be a delay between the transfer start + * time and when DTCn reg contains the new transfer count. Setting + * it explicitly ensures residue is computed correctly at all times. + */ + jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DTC, + jzchan->desc->desc[jzchan->curr_hwdesc].dtc); + /* Write descriptor address and initiate descriptor fetch. */ desc_phys = jzchan->desc->desc_phys + (jzchan->curr_hwdesc * sizeof(*jzchan->desc->desc)); -- cgit v1.2.3 From 83ef4fb7556b6a673f755da670cbacab7e2c7f1b Mon Sep 17 00:00:00 2001 From: Daniel Silsby Date: Wed, 29 Aug 2018 23:32:56 +0200 Subject: dmaengine: dma-jz4780: Further residue status fix Func jz4780_dma_desc_residue() expects the index to the next hw descriptor as its last parameter. Caller func jz4780_dma_tx_status(), however, applied modulus before passing it. When the current hw descriptor was last in the list, the index passed became zero. The resulting excess of reported residue especially caused problems with cyclic DMA transfer clients, i.e. ALSA AIC audio output, which rely on this for determining current DMA location within buffer. Combined with the recent and related residue-reporting fixes, spurious ALSA audio underruns on jz4770 hardware are now fixed. Signed-off-by: Daniel Silsby Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index d3b915ec8a09..b73d96166637 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -653,7 +653,7 @@ static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan, to_jz4780_dma_desc(vdesc), 0); } else if (cookie == jzchan->desc->vdesc.tx.cookie) { txstate->residue = jz4780_dma_desc_residue(jzchan, jzchan->desc, - (jzchan->curr_hwdesc + 1) % jzchan->desc->count); + jzchan->curr_hwdesc + 1); } else txstate->residue = 0; -- cgit v1.2.3 From 1f0b0f235a84f2ffe3ab826359e63c0fe674a619 Mon Sep 17 00:00:00 2001 From: Daniel Silsby Date: Wed, 29 Aug 2018 23:32:57 +0200 Subject: dmaengine: dma-jz4780: Use dma_set_residue() This is the standard method provided by dmaengine header. Signed-off-by: Daniel Silsby Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4780.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index b73d96166637..e1bb93dd32ba 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -639,6 +639,7 @@ static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan, struct virt_dma_desc *vdesc; enum dma_status status; unsigned long flags; + unsigned long residue = 0; status = dma_cookie_status(chan, cookie, txstate); if ((status == DMA_COMPLETE) || (txstate == NULL)) @@ -649,13 +650,13 @@ static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan, vdesc = vchan_find_desc(&jzchan->vchan, cookie); if (vdesc) { /* On the issued list, so hasn't been processed yet */ - txstate->residue = jz4780_dma_desc_residue(jzchan, + residue = jz4780_dma_desc_residue(jzchan, to_jz4780_dma_desc(vdesc), 0); } else if (cookie == jzchan->desc->vdesc.tx.cookie) { - txstate->residue = jz4780_dma_desc_residue(jzchan, jzchan->desc, + residue = jz4780_dma_desc_residue(jzchan, jzchan->desc, jzchan->curr_hwdesc + 1); - } else - txstate->residue = 0; + } + dma_set_residue(txstate, residue); if (vdesc && jzchan->desc && vdesc == &jzchan->desc->vdesc && jzchan->desc->status & (JZ_DMA_DCS_AR | JZ_DMA_DCS_HLT)) -- cgit v1.2.3 From 7645d26f3b35c2bd6e0e1d3867636e5bdad97b1b Mon Sep 17 00:00:00 2001 From: Zhaoxiong Yuan Date: Sat, 8 Sep 2018 06:02:10 +1100 Subject: dmaengine: idma64: replace spin_lock_irqsave with spin_lock idma64_chan_irq() is invoked in hardirq handle function, it is unnecessary to call spin_lock_irqsave. Signed-off-by: Zhaoxiong Yuan Signed-off-by: Vinod Koul --- drivers/dma/idma64.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/idma64.c b/drivers/dma/idma64.c index 1fbf9cb9b742..5b9c1566ad73 100644 --- a/drivers/dma/idma64.c +++ b/drivers/dma/idma64.c @@ -142,9 +142,8 @@ static void idma64_chan_irq(struct idma64 *idma64, unsigned short c, { struct idma64_chan *idma64c = &idma64->chan[c]; struct idma64_desc *desc; - unsigned long flags; - spin_lock_irqsave(&idma64c->vchan.lock, flags); + spin_lock(&idma64c->vchan.lock); desc = idma64c->desc; if (desc) { if (status_err & (1 << c)) { @@ -161,7 +160,7 @@ static void idma64_chan_irq(struct idma64 *idma64, unsigned short c, if (idma64c->desc == NULL || desc->status == DMA_ERROR) idma64_stop_transfer(idma64c); } - spin_unlock_irqrestore(&idma64c->vchan.lock, flags); + spin_unlock(&idma64c->vchan.lock); } static irqreturn_t idma64_irq(int irq, void *dev) -- cgit v1.2.3 From 5b7d0c9474bc3798b38c393974747336a2cf8497 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Fri, 14 Sep 2018 21:13:48 +0200 Subject: dmaengine: mcf-edma: avoid warning for wrong pointer cast This patch fixes the following compilation warning reported during x86_64 allmodconfig build: drivers/dma/mcf-edma.c: In function 'mcf_edma_filter_fn': drivers/dma/mcf-edma.c:296:33: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] return (mcf_chan->slave_id == (u32)param); Reported-by: Stephen Rothwell Signed-off-by: Angelo Dureghello Signed-off-by: Vinod Koul --- drivers/dma/mcf-edma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/mcf-edma.c b/drivers/dma/mcf-edma.c index 4d30d5302649..5de1b07eddff 100644 --- a/drivers/dma/mcf-edma.c +++ b/drivers/dma/mcf-edma.c @@ -293,7 +293,7 @@ bool mcf_edma_filter_fn(struct dma_chan *chan, void *param) if (chan->device->dev->driver == &mcf_edma_driver.driver) { struct fsl_edma_chan *mcf_chan = to_fsl_edma_chan(chan); - return (mcf_chan->slave_id == (u32)param); + return (mcf_chan->slave_id == (uintptr_t)param); } return false; -- cgit v1.2.3 From cfb03be6c7e8a1591285849c361d67b09f5149f7 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Fri, 14 Sep 2018 14:53:32 -0400 Subject: driver/dma/ioat: Call del_timer_sync() without holding prep_lock The following lockdep splat was observed: [ 1222.241750] ====================================================== [ 1222.271301] WARNING: possible circular locking dependency detected [ 1222.301060] 4.16.0-10.el8+5.x86_64+debug #1 Not tainted [ 1222.326659] ------------------------------------------------------ [ 1222.356565] systemd-shutdow/1 is trying to acquire lock: [ 1222.382660] ((&ioat_chan->timer)){+.-.}, at: [<00000000f71e1a28>] del_timer_sync+0x5/0xf0 [ 1222.422928] [ 1222.422928] but task is already holding lock: [ 1222.451743] (&(&ioat_chan->prep_lock)->rlock){+.-.}, at: [<000000008ea98b12>] ioat_shutdown+0x86/0x100 [ioatdma] : [ 1223.524987] Chain exists of: [ 1223.524987] (&ioat_chan->timer) --> &(&ioat_chan->cleanup_lock)->rlock --> &(&ioat_chan->prep_lock)->rlock [ 1223.524987] [ 1223.594082] Possible unsafe locking scenario: [ 1223.594082] [ 1223.622630] CPU0 CPU1 [ 1223.645080] ---- ---- [ 1223.667404] lock(&(&ioat_chan->prep_lock)->rlock); [ 1223.691535] lock(&(&ioat_chan->cleanup_lock)->rlock); [ 1223.728657] lock(&(&ioat_chan->prep_lock)->rlock); [ 1223.765122] lock((&ioat_chan->timer)); [ 1223.784095] [ 1223.784095] *** DEADLOCK *** [ 1223.784095] [ 1223.813492] 4 locks held by systemd-shutdow/1: [ 1223.834677] #0: (reboot_mutex){+.+.}, at: [<0000000056d33456>] SYSC_reboot+0x10f/0x300 [ 1223.873310] #1: (&dev->mutex){....}, at: [<00000000258dfdd7>] device_shutdown+0x1c8/0x660 [ 1223.913604] #2: (&dev->mutex){....}, at: [<0000000068331147>] device_shutdown+0x1d6/0x660 [ 1223.954000] #3: (&(&ioat_chan->prep_lock)->rlock){+.-.}, at: [<000000008ea98b12>] ioat_shutdown+0x86/0x100 [ioatdma] In the ioat_shutdown() function: spin_lock_bh(&ioat_chan->prep_lock); set_bit(IOAT_CHAN_DOWN, &ioat_chan->state); del_timer_sync(&ioat_chan->timer); spin_unlock_bh(&ioat_chan->prep_lock); According to the synchronization rule for the del_timer_sync() function, the caller must not hold locks which would prevent completion of the timer's handler. The timer structure has its own lock that manages its synchronization. Setting the IOAT_CHAN_DOWN bit should prevent other CPUs from trying to use that device anyway, there is probably no need to call del_timer_sync() while holding the prep_lock. So the del_timer_sync() call is now moved outside of the prep_lock critical section to prevent the circular lock dependency. Signed-off-by: Waiman Long Reviewed-by: Dave Jiang Signed-off-by: Vinod Koul --- drivers/dma/ioat/init.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c index 4fa4c06c9edb..21a5708985bc 100644 --- a/drivers/dma/ioat/init.c +++ b/drivers/dma/ioat/init.c @@ -1205,8 +1205,15 @@ static void ioat_shutdown(struct pci_dev *pdev) spin_lock_bh(&ioat_chan->prep_lock); set_bit(IOAT_CHAN_DOWN, &ioat_chan->state); - del_timer_sync(&ioat_chan->timer); spin_unlock_bh(&ioat_chan->prep_lock); + /* + * Synchronization rule for del_timer_sync(): + * - The caller must not hold locks which would prevent + * completion of the timer's handler. + * So prep_lock cannot be held before calling it. + */ + del_timer_sync(&ioat_chan->timer); + /* this should quiesce then reset */ ioat_reset_hw(ioat_chan); } -- cgit v1.2.3 From 5e621f5d538985f010035c6f3e28c22829d36db1 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 11 Sep 2018 16:20:25 -0700 Subject: dmaengine: timb_dma: Use proper enum in td_prep_slave_sg Clang warns when implicitly converting from one enumerated type to another. Avoid this by using the equivalent value from the expected type. drivers/dma/timb_dma.c:548:27: warning: implicit conversion from enumeration type 'enum dma_transfer_direction' to different enumeration type 'enum dma_data_direction' [-Wenum-conversion] td_desc->desc_list_len, DMA_MEM_TO_DEV); ^~~~~~~~~~~~~~ 1 warning generated. Reported-by: Nick Desaulniers Signed-off-by: Nathan Chancellor Reviewed-by: Nick Desaulniers Signed-off-by: Vinod Koul --- drivers/dma/timb_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index 395c698edb4d..fc0f9c8766a8 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -545,7 +545,7 @@ static struct dma_async_tx_descriptor *td_prep_slave_sg(struct dma_chan *chan, } dma_sync_single_for_device(chan2dmadev(chan), td_desc->txd.phys, - td_desc->desc_list_len, DMA_MEM_TO_DEV); + td_desc->desc_list_len, DMA_TO_DEVICE); return &td_desc->txd; } -- cgit v1.2.3 From cc832dc8e32785a730ba07c3a357e17c201a5df8 Mon Sep 17 00:00:00 2001 From: Pierre-Yves MORDRET Date: Tue, 11 Sep 2018 09:31:16 +0200 Subject: dmaengine: stm32-dma: check whether length is aligned on FIFO threshold When a period length is not multiple of FIFO some data may be stuck within FIFO. Burst/FIFO Threshold/Period or buffer length check has to be hardened In any case DMA will grant any request from client but will degraded any parameters whether awkward. Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Vinod Koul --- drivers/dma/stm32-dma.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index 379e8d534e61..4903a408fc14 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -308,20 +308,12 @@ static bool stm32_dma_fifo_threshold_is_allowed(u32 burst, u32 threshold, static bool stm32_dma_is_burst_possible(u32 buf_len, u32 threshold) { - switch (threshold) { - case STM32_DMA_FIFO_THRESHOLD_FULL: - if (buf_len >= STM32_DMA_MAX_BURST) - return true; - else - return false; - case STM32_DMA_FIFO_THRESHOLD_HALFFULL: - if (buf_len >= STM32_DMA_MAX_BURST / 2) - return true; - else - return false; - default: - return false; - } + /* + * Buffer or period length has to be aligned on FIFO depth. + * Otherwise bytes may be stuck within FIFO at buffer or period + * length. + */ + return ((buf_len % ((threshold + 1) * 4)) == 0); } static u32 stm32_dma_get_best_burst(u32 buf_len, u32 max_burst, u32 threshold, -- cgit v1.2.3 From f4d34aa8c887a8a2d23ef546da0efa10e3f77241 Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Fri, 5 Oct 2018 00:03:10 +0300 Subject: dmaengine: ioat: fix prototype of ioat_enumerate_channels Signed-off-by: Rami Rosen Signed-off-by: Vinod Koul --- drivers/dma/ioat/init.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c index 21a5708985bc..0fec3c554fe3 100644 --- a/drivers/dma/ioat/init.c +++ b/drivers/dma/ioat/init.c @@ -129,7 +129,7 @@ static void ioat_init_channel(struct ioatdma_device *ioat_dma, struct ioatdma_chan *ioat_chan, int idx); static void ioat_intr_quirk(struct ioatdma_device *ioat_dma); -static int ioat_enumerate_channels(struct ioatdma_device *ioat_dma); +static void ioat_enumerate_channels(struct ioatdma_device *ioat_dma); static int ioat3_dma_self_test(struct ioatdma_device *ioat_dma); static int ioat_dca_enabled = 1; @@ -575,7 +575,7 @@ static void ioat_dma_remove(struct ioatdma_device *ioat_dma) * ioat_enumerate_channels - find and initialize the device's channels * @ioat_dma: the ioat dma device to be enumerated */ -static int ioat_enumerate_channels(struct ioatdma_device *ioat_dma) +static void ioat_enumerate_channels(struct ioatdma_device *ioat_dma) { struct ioatdma_chan *ioat_chan; struct device *dev = &ioat_dma->pdev->dev; @@ -594,7 +594,7 @@ static int ioat_enumerate_channels(struct ioatdma_device *ioat_dma) xfercap_log = readb(ioat_dma->reg_base + IOAT_XFERCAP_OFFSET); xfercap_log &= 0x1f; /* bits [4:0] valid */ if (xfercap_log == 0) - return 0; + return; dev_dbg(dev, "%s: xfercap = %d\n", __func__, 1 << xfercap_log); for (i = 0; i < dma->chancnt; i++) { @@ -611,7 +611,6 @@ static int ioat_enumerate_channels(struct ioatdma_device *ioat_dma) } } dma->chancnt = i; - return i; } /** -- cgit v1.2.3 From d64e1b3f5cce41dd878a3ea008dd696a4d7d07d9 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Sat, 29 Sep 2018 13:16:36 +0530 Subject: dmaengine: owl: Add Slave and Cyclic mode support for Actions Semi Owl S900 SoC Add Slave and Cyclic mode support for Actions Semi Owl S900 SoC. The slave mode supports bus width of 4 bytes common for all peripherals and 1 byte specific for UART. The cyclic mode supports only block mode transfer. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Vinod Koul --- drivers/dma/owl-dma.c | 279 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 272 insertions(+), 7 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c index 7812a6338acd..1d26db4c9229 100644 --- a/drivers/dma/owl-dma.c +++ b/drivers/dma/owl-dma.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "virt-dma.h" @@ -165,6 +166,7 @@ struct owl_dma_lli { struct owl_dma_txd { struct virt_dma_desc vd; struct list_head lli_list; + bool cyclic; }; /** @@ -191,6 +193,8 @@ struct owl_dma_vchan { struct virt_dma_chan vc; struct owl_dma_pchan *pchan; struct owl_dma_txd *txd; + struct dma_slave_config cfg; + u8 drq; }; /** @@ -336,9 +340,11 @@ static struct owl_dma_lli *owl_dma_alloc_lli(struct owl_dma *od) static struct owl_dma_lli *owl_dma_add_lli(struct owl_dma_txd *txd, struct owl_dma_lli *prev, - struct owl_dma_lli *next) + struct owl_dma_lli *next, + bool is_cyclic) { - list_add_tail(&next->node, &txd->lli_list); + if (!is_cyclic) + list_add_tail(&next->node, &txd->lli_list); if (prev) { prev->hw.next_lli = next->phys; @@ -351,7 +357,9 @@ static struct owl_dma_lli *owl_dma_add_lli(struct owl_dma_txd *txd, static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan, struct owl_dma_lli *lli, dma_addr_t src, dma_addr_t dst, - u32 len, enum dma_transfer_direction dir) + u32 len, enum dma_transfer_direction dir, + struct dma_slave_config *sconfig, + bool is_cyclic) { struct owl_dma_lli_hw *hw = &lli->hw; u32 mode; @@ -364,6 +372,32 @@ static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan, OWL_DMA_MODE_DT_DCU | OWL_DMA_MODE_SAM_INC | OWL_DMA_MODE_DAM_INC; + break; + case DMA_MEM_TO_DEV: + mode |= OWL_DMA_MODE_TS(vchan->drq) + | OWL_DMA_MODE_ST_DCU | OWL_DMA_MODE_DT_DEV + | OWL_DMA_MODE_SAM_INC | OWL_DMA_MODE_DAM_CONST; + + /* + * Hardware only supports 32bit and 8bit buswidth. Since the + * default is 32bit, select 8bit only when requested. + */ + if (sconfig->dst_addr_width == DMA_SLAVE_BUSWIDTH_1_BYTE) + mode |= OWL_DMA_MODE_NDDBW_8BIT; + + break; + case DMA_DEV_TO_MEM: + mode |= OWL_DMA_MODE_TS(vchan->drq) + | OWL_DMA_MODE_ST_DEV | OWL_DMA_MODE_DT_DCU + | OWL_DMA_MODE_SAM_CONST | OWL_DMA_MODE_DAM_INC; + + /* + * Hardware only supports 32bit and 8bit buswidth. Since the + * default is 32bit, select 8bit only when requested. + */ + if (sconfig->src_addr_width == DMA_SLAVE_BUSWIDTH_1_BYTE) + mode |= OWL_DMA_MODE_NDDBW_8BIT; + break; default: return -EINVAL; @@ -381,7 +415,10 @@ static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan, OWL_DMA_LLC_SAV_LOAD_NEXT | OWL_DMA_LLC_DAV_LOAD_NEXT); - hw->ctrlb = llc_hw_ctrlb(OWL_DMA_INTCTL_SUPER_BLOCK); + if (is_cyclic) + hw->ctrlb = llc_hw_ctrlb(OWL_DMA_INTCTL_BLOCK); + else + hw->ctrlb = llc_hw_ctrlb(OWL_DMA_INTCTL_SUPER_BLOCK); return 0; } @@ -443,6 +480,16 @@ static void owl_dma_terminate_pchan(struct owl_dma *od, spin_unlock_irqrestore(&od->lock, flags); } +static void owl_dma_pause_pchan(struct owl_dma_pchan *pchan) +{ + pchan_writel(pchan, 1, OWL_DMAX_PAUSE); +} + +static void owl_dma_resume_pchan(struct owl_dma_pchan *pchan) +{ + pchan_writel(pchan, 0, OWL_DMAX_PAUSE); +} + static int owl_dma_start_next_txd(struct owl_dma_vchan *vchan) { struct owl_dma *od = to_owl_dma(vchan->vc.chan.device); @@ -464,7 +511,10 @@ static int owl_dma_start_next_txd(struct owl_dma_vchan *vchan) lli = list_first_entry(&txd->lli_list, struct owl_dma_lli, node); - int_ctl = OWL_DMA_INTCTL_SUPER_BLOCK; + if (txd->cyclic) + int_ctl = OWL_DMA_INTCTL_BLOCK; + else + int_ctl = OWL_DMA_INTCTL_SUPER_BLOCK; pchan_writel(pchan, OWL_DMAX_MODE, OWL_DMA_MODE_LME); pchan_writel(pchan, OWL_DMAX_LINKLIST_CTL, @@ -627,6 +677,54 @@ static int owl_dma_terminate_all(struct dma_chan *chan) return 0; } +static int owl_dma_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct owl_dma_vchan *vchan = to_owl_vchan(chan); + + /* Reject definitely invalid configurations */ + if (config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || + config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) + return -EINVAL; + + memcpy(&vchan->cfg, config, sizeof(struct dma_slave_config)); + + return 0; +} + +static int owl_dma_pause(struct dma_chan *chan) +{ + struct owl_dma_vchan *vchan = to_owl_vchan(chan); + unsigned long flags; + + spin_lock_irqsave(&vchan->vc.lock, flags); + + owl_dma_pause_pchan(vchan->pchan); + + spin_unlock_irqrestore(&vchan->vc.lock, flags); + + return 0; +} + +static int owl_dma_resume(struct dma_chan *chan) +{ + struct owl_dma_vchan *vchan = to_owl_vchan(chan); + unsigned long flags; + + if (!vchan->pchan && !vchan->txd) + return 0; + + dev_dbg(chan2dev(chan), "vchan %p: resume\n", &vchan->vc); + + spin_lock_irqsave(&vchan->vc.lock, flags); + + owl_dma_resume_pchan(vchan->pchan); + + spin_unlock_irqrestore(&vchan->vc.lock, flags); + + return 0; +} + static u32 owl_dma_getbytes_chan(struct owl_dma_vchan *vchan) { struct owl_dma_pchan *pchan; @@ -754,13 +852,14 @@ static struct dma_async_tx_descriptor bytes = min_t(size_t, (len - offset), OWL_DMA_FRAME_MAX_LENGTH); ret = owl_dma_cfg_lli(vchan, lli, src + offset, dst + offset, - bytes, DMA_MEM_TO_MEM); + bytes, DMA_MEM_TO_MEM, + &vchan->cfg, txd->cyclic); if (ret) { dev_warn(chan2dev(chan), "failed to config lli\n"); goto err_txd_free; } - prev = owl_dma_add_lli(txd, prev, lli); + prev = owl_dma_add_lli(txd, prev, lli, false); } return vchan_tx_prep(&vchan->vc, &txd->vd, flags); @@ -770,6 +869,133 @@ err_txd_free: return NULL; } +static struct dma_async_tx_descriptor + *owl_dma_prep_slave_sg(struct dma_chan *chan, + struct scatterlist *sgl, + unsigned int sg_len, + enum dma_transfer_direction dir, + unsigned long flags, void *context) +{ + struct owl_dma *od = to_owl_dma(chan->device); + struct owl_dma_vchan *vchan = to_owl_vchan(chan); + struct dma_slave_config *sconfig = &vchan->cfg; + struct owl_dma_txd *txd; + struct owl_dma_lli *lli, *prev = NULL; + struct scatterlist *sg; + dma_addr_t addr, src = 0, dst = 0; + size_t len; + int ret, i; + + txd = kzalloc(sizeof(*txd), GFP_NOWAIT); + if (!txd) + return NULL; + + INIT_LIST_HEAD(&txd->lli_list); + + for_each_sg(sgl, sg, sg_len, i) { + addr = sg_dma_address(sg); + len = sg_dma_len(sg); + + if (len > OWL_DMA_FRAME_MAX_LENGTH) { + dev_err(od->dma.dev, + "frame length exceeds max supported length"); + goto err_txd_free; + } + + lli = owl_dma_alloc_lli(od); + if (!lli) { + dev_err(chan2dev(chan), "failed to allocate lli"); + goto err_txd_free; + } + + if (dir == DMA_MEM_TO_DEV) { + src = addr; + dst = sconfig->dst_addr; + } else { + src = sconfig->src_addr; + dst = addr; + } + + ret = owl_dma_cfg_lli(vchan, lli, src, dst, len, dir, sconfig, + txd->cyclic); + if (ret) { + dev_warn(chan2dev(chan), "failed to config lli"); + goto err_txd_free; + } + + prev = owl_dma_add_lli(txd, prev, lli, false); + } + + return vchan_tx_prep(&vchan->vc, &txd->vd, flags); + +err_txd_free: + owl_dma_free_txd(od, txd); + + return NULL; +} + +static struct dma_async_tx_descriptor + *owl_prep_dma_cyclic(struct dma_chan *chan, + dma_addr_t buf_addr, size_t buf_len, + size_t period_len, + enum dma_transfer_direction dir, + unsigned long flags) +{ + struct owl_dma *od = to_owl_dma(chan->device); + struct owl_dma_vchan *vchan = to_owl_vchan(chan); + struct dma_slave_config *sconfig = &vchan->cfg; + struct owl_dma_txd *txd; + struct owl_dma_lli *lli, *prev = NULL, *first = NULL; + dma_addr_t src = 0, dst = 0; + unsigned int periods = buf_len / period_len; + int ret, i; + + txd = kzalloc(sizeof(*txd), GFP_NOWAIT); + if (!txd) + return NULL; + + INIT_LIST_HEAD(&txd->lli_list); + txd->cyclic = true; + + for (i = 0; i < periods; i++) { + lli = owl_dma_alloc_lli(od); + if (!lli) { + dev_warn(chan2dev(chan), "failed to allocate lli"); + goto err_txd_free; + } + + if (dir == DMA_MEM_TO_DEV) { + src = buf_addr + (period_len * i); + dst = sconfig->dst_addr; + } else if (dir == DMA_DEV_TO_MEM) { + src = sconfig->src_addr; + dst = buf_addr + (period_len * i); + } + + ret = owl_dma_cfg_lli(vchan, lli, src, dst, period_len, + dir, sconfig, txd->cyclic); + if (ret) { + dev_warn(chan2dev(chan), "failed to config lli"); + goto err_txd_free; + } + + if (!first) + first = lli; + + prev = owl_dma_add_lli(txd, prev, lli, false); + } + + /* close the cyclic list */ + owl_dma_add_lli(txd, prev, first, true); + + return vchan_tx_prep(&vchan->vc, &txd->vd, flags); + +err_txd_free: + owl_dma_free_txd(od, txd); + + return NULL; +} + static void owl_dma_free_chan_resources(struct dma_chan *chan) { struct owl_dma_vchan *vchan = to_owl_vchan(chan); @@ -790,6 +1016,27 @@ static inline void owl_dma_free(struct owl_dma *od) } } +static struct dma_chan *owl_dma_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct owl_dma *od = ofdma->of_dma_data; + struct owl_dma_vchan *vchan; + struct dma_chan *chan; + u8 drq = dma_spec->args[0]; + + if (drq > od->nr_vchans) + return NULL; + + chan = dma_get_any_slave_channel(&od->dma); + if (!chan) + return NULL; + + vchan = to_owl_vchan(chan); + vchan->drq = drq; + + return chan; +} + static int owl_dma_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -833,12 +1080,19 @@ static int owl_dma_probe(struct platform_device *pdev) spin_lock_init(&od->lock); dma_cap_set(DMA_MEMCPY, od->dma.cap_mask); + dma_cap_set(DMA_SLAVE, od->dma.cap_mask); + dma_cap_set(DMA_CYCLIC, od->dma.cap_mask); od->dma.dev = &pdev->dev; od->dma.device_free_chan_resources = owl_dma_free_chan_resources; od->dma.device_tx_status = owl_dma_tx_status; od->dma.device_issue_pending = owl_dma_issue_pending; od->dma.device_prep_dma_memcpy = owl_dma_prep_memcpy; + od->dma.device_prep_slave_sg = owl_dma_prep_slave_sg; + od->dma.device_prep_dma_cyclic = owl_prep_dma_cyclic; + od->dma.device_config = owl_dma_config; + od->dma.device_pause = owl_dma_pause; + od->dma.device_resume = owl_dma_resume; od->dma.device_terminate_all = owl_dma_terminate_all; od->dma.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); od->dma.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); @@ -910,8 +1164,18 @@ static int owl_dma_probe(struct platform_device *pdev) goto err_pool_free; } + /* Device-tree DMA controller registration */ + ret = of_dma_controller_register(pdev->dev.of_node, + owl_dma_of_xlate, od); + if (ret) { + dev_err(&pdev->dev, "of_dma_controller_register failed\n"); + goto err_dma_unregister; + } + return 0; +err_dma_unregister: + dma_async_device_unregister(&od->dma); err_pool_free: clk_disable_unprepare(od->clk); dma_pool_destroy(od->lli_pool); @@ -923,6 +1187,7 @@ static int owl_dma_remove(struct platform_device *pdev) { struct owl_dma *od = platform_get_drvdata(pdev); + of_dma_controller_free(pdev->dev.of_node); dma_async_device_unregister(&od->dma); /* Mask all interrupts for this execution environment */ -- cgit v1.2.3 From 6235588705edbd207c23fb2cfc1795b9579797c1 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 19 Jul 2018 22:22:26 +0530 Subject: dmaengine: at_hdmac: remove dma_slave_config direction usage dma_slave_config direction was marked as deprecated quite some time back, remove the usage from this driver so that the field can be removed Acked-by: Ludovic Desroches Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 75f38d19fcbe..7cbac6e8c113 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1320,7 +1320,7 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, if (unlikely(!is_slave_direction(direction))) goto err_out; - if (sconfig->direction == DMA_MEM_TO_DEV) + if (direction == DMA_MEM_TO_DEV) reg_width = convert_buswidth(sconfig->dst_addr_width); else reg_width = convert_buswidth(sconfig->src_addr_width); -- cgit v1.2.3 From 00648f4d0f4156b747de272e962991e5dca2d603 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 19 Jul 2018 22:22:26 +0530 Subject: dmaengine: bcm2835: remove dma_slave_config direction usage dma_slave_config direction was marked as deprecated quite some time back, remove the usage from this driver so that the field can be removed Acked-by: Scott Branden Signed-off-by: Vinod Koul --- drivers/dma/bcm2835-dma.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 847f84a41a69..cad55ab80d41 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -778,14 +778,6 @@ static int bcm2835_dma_slave_config(struct dma_chan *chan, { struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); - if ((cfg->direction == DMA_DEV_TO_MEM && - cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || - (cfg->direction == DMA_MEM_TO_DEV && - cfg->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || - !is_slave_direction(cfg->direction)) { - return -EINVAL; - } - c->cfg = *cfg; return 0; -- cgit v1.2.3 From 80ade4beb7330f4c6c855619d21062190303a5a3 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 19 Jul 2018 22:22:26 +0530 Subject: dmaengine: coh901318: remove dma_slave_config direction usage dma_slave_config direction was marked as deprecated quite some time back, remove the usage from this driver so that the field can be removed Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/coh901318.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index da74fd74636b..eebaba3d9e78 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -1306,6 +1306,7 @@ struct coh901318_chan { unsigned long nbr_active_done; unsigned long busy; + struct dma_slave_config config; u32 addr; u32 ctrl; @@ -1402,6 +1403,10 @@ static inline struct coh901318_chan *to_coh901318_chan(struct dma_chan *chan) return container_of(chan, struct coh901318_chan, chan); } +static int coh901318_dma_set_runtimeconfig(struct dma_chan *chan, + struct dma_slave_config *config, + enum dma_transfer_direction direction); + static inline const struct coh901318_params * cohc_chan_param(struct coh901318_chan *cohc) { @@ -2360,6 +2365,8 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (lli == NULL) goto err_dma_alloc; + coh901318_dma_set_runtimeconfig(chan, &cohc->config, direction); + /* initiate allocated lli list */ ret = coh901318_lli_fill_sg(&cohc->base->pool, lli, sgl, sg_len, cohc->addr, @@ -2499,7 +2506,8 @@ static const struct burst_table burst_sizes[] = { }; static int coh901318_dma_set_runtimeconfig(struct dma_chan *chan, - struct dma_slave_config *config) + struct dma_slave_config *config, + enum dma_transfer_direction direction) { struct coh901318_chan *cohc = to_coh901318_chan(chan); dma_addr_t addr; @@ -2509,11 +2517,11 @@ static int coh901318_dma_set_runtimeconfig(struct dma_chan *chan, int i = 0; /* We only support mem to per or per to mem transfers */ - if (config->direction == DMA_DEV_TO_MEM) { + if (direction == DMA_DEV_TO_MEM) { addr = config->src_addr; addr_width = config->src_addr_width; maxburst = config->src_maxburst; - } else if (config->direction == DMA_MEM_TO_DEV) { + } else if (direction == DMA_MEM_TO_DEV) { addr = config->dst_addr; addr_width = config->dst_addr_width; maxburst = config->dst_maxburst; @@ -2579,6 +2587,16 @@ static int coh901318_dma_set_runtimeconfig(struct dma_chan *chan, return 0; } +static int coh901318_dma_slave_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct coh901318_chan *cohc = to_coh901318_chan(chan); + + memcpy(&cohc->config, config, sizeof(*config)); + + return 0; +} + static void coh901318_base_init(struct dma_device *dma, const int *pick_chans, struct coh901318_base *base) { @@ -2684,7 +2702,7 @@ static int __init coh901318_probe(struct platform_device *pdev) base->dma_slave.device_prep_slave_sg = coh901318_prep_slave_sg; base->dma_slave.device_tx_status = coh901318_tx_status; base->dma_slave.device_issue_pending = coh901318_issue_pending; - base->dma_slave.device_config = coh901318_dma_set_runtimeconfig; + base->dma_slave.device_config = coh901318_dma_slave_config; base->dma_slave.device_pause = coh901318_pause; base->dma_slave.device_resume = coh901318_resume; base->dma_slave.device_terminate_all = coh901318_terminate_all; @@ -2707,7 +2725,7 @@ static int __init coh901318_probe(struct platform_device *pdev) base->dma_memcpy.device_prep_dma_memcpy = coh901318_prep_memcpy; base->dma_memcpy.device_tx_status = coh901318_tx_status; base->dma_memcpy.device_issue_pending = coh901318_issue_pending; - base->dma_memcpy.device_config = coh901318_dma_set_runtimeconfig; + base->dma_memcpy.device_config = coh901318_dma_slave_config; base->dma_memcpy.device_pause = coh901318_pause; base->dma_memcpy.device_resume = coh901318_resume; base->dma_memcpy.device_terminate_all = coh901318_terminate_all; -- cgit v1.2.3 From 09347e3f26a56bd5abe931251547aac8d80f0e7e Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 19 Jul 2018 22:22:26 +0530 Subject: dmaengine: jz4740: remove dma_slave_config direction usage dma_slave_config direction was marked as deprecated quite some time back, remove the usage from this driver so that the field can be removed Signed-off-by: Vinod Koul --- drivers/dma/dma-jz4740.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c index afd5e10f8927..5253e3c0dc04 100644 --- a/drivers/dma/dma-jz4740.c +++ b/drivers/dma/dma-jz4740.c @@ -113,6 +113,7 @@ struct jz4740_dma_desc { struct jz4740_dmaengine_chan { struct virt_dma_chan vchan; unsigned int id; + struct dma_slave_config config; dma_addr_t fifo_addr; unsigned int transfer_shift; @@ -203,8 +204,9 @@ static enum jz4740_dma_transfer_size jz4740_dma_maxburst(u32 maxburst) return JZ4740_DMA_TRANSFER_SIZE_32BYTE; } -static int jz4740_dma_slave_config(struct dma_chan *c, - struct dma_slave_config *config) +static int jz4740_dma_slave_config_write(struct dma_chan *c, + struct dma_slave_config *config, + enum dma_transfer_direction direction) { struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); @@ -214,7 +216,7 @@ static int jz4740_dma_slave_config(struct dma_chan *c, enum jz4740_dma_flags flags; uint32_t cmd; - switch (config->direction) { + switch (direction) { case DMA_MEM_TO_DEV: flags = JZ4740_DMA_SRC_AUTOINC; transfer_size = jz4740_dma_maxburst(config->dst_maxburst); @@ -265,6 +267,15 @@ static int jz4740_dma_slave_config(struct dma_chan *c, return 0; } +static int jz4740_dma_slave_config(struct dma_chan *c, + struct dma_slave_config *config) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + + memcpy(&chan->config, config, sizeof(*config)); + return 0; +} + static int jz4740_dma_terminate_all(struct dma_chan *c) { struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); @@ -407,6 +418,8 @@ static struct dma_async_tx_descriptor *jz4740_dma_prep_slave_sg( desc->direction = direction; desc->cyclic = false; + jz4740_dma_slave_config_write(c, &chan->config, direction); + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); } @@ -438,6 +451,8 @@ static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic( desc->direction = direction; desc->cyclic = true; + jz4740_dma_slave_config_write(c, &chan->config, direction); + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); } -- cgit v1.2.3 From 3d143c252ef014f38e6eacb548d5b9464c093e4c Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 19 Jul 2018 22:22:26 +0530 Subject: dmaengine: dw: remove dma_slave_config direction usage dma_slave_config direction was marked as deprecated quite some time back, remove the usage from this driver so that the field can be removed Acked-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index f43e6dafe446..d0c3e50b39fb 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -886,12 +886,7 @@ static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig) */ u32 s = dw->pdata->is_idma32 ? 1 : 2; - /* Check if chan will be configured for slave transfers */ - if (!is_slave_direction(sconfig->direction)) - return -EINVAL; - memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); - dwc->direction = sconfig->direction; sc->src_maxburst = sc->src_maxburst > 1 ? fls(sc->src_maxburst) - s : 0; sc->dst_maxburst = sc->dst_maxburst > 1 ? fls(sc->dst_maxburst) - s : 0; -- cgit v1.2.3 From fe6d3719970e5b6662dbdb6e10d71eac5d5b8236 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 19 Jul 2018 22:22:26 +0530 Subject: dmaengine: hsu: remove dma_slave_config direction usage dma_slave_config direction was marked as deprecated quite some time back, remove the usage from this driver so that the field can be removed Acked-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/hsu/hsu.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/hsu/hsu.c b/drivers/dma/hsu/hsu.c index 202ffa9f7611..e06f20272fd7 100644 --- a/drivers/dma/hsu/hsu.c +++ b/drivers/dma/hsu/hsu.c @@ -348,10 +348,6 @@ static int hsu_dma_slave_config(struct dma_chan *chan, { struct hsu_dma_chan *hsuc = to_hsu_dma_chan(chan); - /* Check if chan will be configured for slave transfers */ - if (!is_slave_direction(config->direction)) - return -EINVAL; - memcpy(&hsuc->config, config, sizeof(hsuc->config)); return 0; -- cgit v1.2.3 From 7f0c145089537125ba91852eddebe13b2c1266fb Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 19 Jul 2018 22:22:26 +0530 Subject: dmaengine: idma: remove dma_slave_config direction usage dma_slave_config direction was marked as deprecated quite some time back, remove the usage from this driver so that the field can be removed Acked-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/idma64.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/idma64.c b/drivers/dma/idma64.c index 1fbf9cb9b742..40dc5d2ddf3d 100644 --- a/drivers/dma/idma64.c +++ b/drivers/dma/idma64.c @@ -408,10 +408,6 @@ static int idma64_slave_config(struct dma_chan *chan, { struct idma64_chan *idma64c = to_idma64_chan(chan); - /* Check if chan will be configured for slave transfers */ - if (!is_slave_direction(config->direction)) - return -EINVAL; - memcpy(&idma64c->config, config, sizeof(idma64c->config)); convert_burst(&idma64c->config.src_maxburst); -- cgit v1.2.3 From dea7a9fbb009ba0629c44fcf105f2d8b0ff74e9f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 19 Jul 2018 22:22:26 +0530 Subject: dmaengine: imx-dma: remove dma_slave_config direction usage dma_slave_config direction was marked as deprecated quite some time back, remove the usage from this driver so that the field can be removed Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 75b6ff0415ee..c2fff3f6c9ca 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -162,6 +162,7 @@ struct imxdma_channel { bool enabled_2d; int slot_2d; unsigned int irq; + struct dma_slave_config config; }; enum imx_dma_type { @@ -675,14 +676,15 @@ static int imxdma_terminate_all(struct dma_chan *chan) return 0; } -static int imxdma_config(struct dma_chan *chan, - struct dma_slave_config *dmaengine_cfg) +static int imxdma_config_write(struct dma_chan *chan, + struct dma_slave_config *dmaengine_cfg, + enum dma_transfer_direction direction) { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct imxdma_engine *imxdma = imxdmac->imxdma; unsigned int mode = 0; - if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { + if (direction == DMA_DEV_TO_MEM) { imxdmac->per_address = dmaengine_cfg->src_addr; imxdmac->watermark_level = dmaengine_cfg->src_maxburst; imxdmac->word_size = dmaengine_cfg->src_addr_width; @@ -723,6 +725,16 @@ static int imxdma_config(struct dma_chan *chan, return 0; } +static int imxdma_config(struct dma_chan *chan, + struct dma_slave_config *dmaengine_cfg) +{ + struct imxdma_channel *imxdmac = to_imxdma_chan(chan); + + memcpy(&imxdmac->config, dmaengine_cfg, sizeof(*dmaengine_cfg)); + + return 0; +} + static enum dma_status imxdma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) @@ -905,6 +917,8 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( desc->desc.callback = NULL; desc->desc.callback_param = NULL; + imxdma_config_write(chan, &imxdmac->config, direction); + return &desc->desc; } -- cgit v1.2.3 From ad7756e7775e1af42ec0032ea0bea968b8f343a7 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 5 Oct 2018 06:32:12 +0530 Subject: dmaengine: k3dma: dont use direction for memcpy Memcpy has no direction (copy from memory to memory) so remove the check in prep_memcpy() Tested-by: John Stultz Signed-off-by: Vinod Koul --- drivers/dma/k3dma.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index 6bfa217ed6d0..964dcb46755b 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -501,14 +501,8 @@ static struct dma_async_tx_descriptor *k3_dma_prep_memcpy( copy = min_t(size_t, len, DMA_MAX_SIZE); k3_dma_fill_desc(ds, dst, src, copy, num++, c->ccfg); - if (c->dir == DMA_MEM_TO_DEV) { - src += copy; - } else if (c->dir == DMA_DEV_TO_MEM) { - dst += copy; - } else { - src += copy; - dst += copy; - } + src += copy; + dst += copy; len -= copy; } while (len); -- cgit v1.2.3 From 2ae1a237e58a4e1da4505274e687b37201020d50 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 19 Jul 2018 22:22:26 +0530 Subject: dmaengine: k3dma: remove dma_slave_config direction usage dma_slave_config direction was marked as deprecated quite some time back, remove the usage from this driver so that the field can be removed Tested-by: John Stultz Signed-off-by: Vinod Koul --- drivers/dma/k3dma.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index 964dcb46755b..fdec2b6cfbb0 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -87,10 +87,10 @@ struct k3_dma_chan { struct virt_dma_chan vc; struct k3_dma_phy *phy; struct list_head node; - enum dma_transfer_direction dir; dma_addr_t dev_addr; enum dma_status status; bool cyclic; + struct dma_slave_config slave_config; }; struct k3_dma_phy { @@ -118,6 +118,10 @@ struct k3_dma_dev { #define to_k3_dma(dmadev) container_of(dmadev, struct k3_dma_dev, slave) +static int k3_dma_config_write(struct dma_chan *chan, + enum dma_transfer_direction dir, + struct dma_slave_config *cfg); + static struct k3_dma_chan *to_k3_chan(struct dma_chan *chan) { return container_of(chan, struct k3_dma_chan, vc.chan); @@ -536,6 +540,7 @@ static struct dma_async_tx_descriptor *k3_dma_prep_slave_sg( if (!ds) return NULL; num = 0; + k3_dma_config_write(chan, dir, &c->slave_config); for_each_sg(sgl, sg, sglen, i) { addr = sg_dma_address(sg); @@ -596,6 +601,7 @@ k3_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, avail = buf_len; total = avail; num = 0; + k3_dma_config_write(chan, dir, &c->slave_config); if (period_len < modulo) modulo = period_len; @@ -636,18 +642,26 @@ static int k3_dma_config(struct dma_chan *chan, struct dma_slave_config *cfg) { struct k3_dma_chan *c = to_k3_chan(chan); + + memcpy(&c->slave_config, cfg, sizeof(*cfg)); + + return 0; +} + +static int k3_dma_config_write(struct dma_chan *chan, + enum dma_transfer_direction dir, + struct dma_slave_config *cfg) +{ + struct k3_dma_chan *c = to_k3_chan(chan); u32 maxburst = 0, val = 0; enum dma_slave_buswidth width = DMA_SLAVE_BUSWIDTH_UNDEFINED; - if (cfg == NULL) - return -EINVAL; - c->dir = cfg->direction; - if (c->dir == DMA_DEV_TO_MEM) { + if (dir == DMA_DEV_TO_MEM) { c->ccfg = CX_CFG_DSTINCR; c->dev_addr = cfg->src_addr; maxburst = cfg->src_maxburst; width = cfg->src_addr_width; - } else if (c->dir == DMA_MEM_TO_DEV) { + } else if (dir == DMA_MEM_TO_DEV) { c->ccfg = CX_CFG_SRCINCR; c->dev_addr = cfg->dst_addr; maxburst = cfg->dst_maxburst; -- cgit v1.2.3 From 4e3c40408ab8dc4ac4ff5b2b5fe71bd4a8f97f85 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 19 Jul 2018 22:22:26 +0530 Subject: dmaengine: ep93xx_dma: remove dma_slave_config direction usage dma_slave_config direction was marked as deprecated quite some time back, remove the usage from this driver so that the field can be removed Signed-off-by: Vinod Koul --- drivers/dma/ep93xx_dma.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index a15592383d4e..f674eb5fbbef 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -109,6 +109,9 @@ #define DMA_MAX_CHAN_DESCRIPTORS 32 struct ep93xx_dma_engine; +static int ep93xx_dma_slave_config_write(struct dma_chan *chan, + enum dma_transfer_direction dir, + struct dma_slave_config *config); /** * struct ep93xx_dma_desc - EP93xx specific transaction descriptor @@ -180,6 +183,7 @@ struct ep93xx_dma_chan { struct list_head free_list; u32 runtime_addr; u32 runtime_ctrl; + struct dma_slave_config slave_config; }; /** @@ -1051,6 +1055,8 @@ ep93xx_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return NULL; } + ep93xx_dma_slave_config_write(chan, dir, &edmac->slave_config); + first = NULL; for_each_sg(sgl, sg, sg_len, i) { size_t len = sg_dma_len(sg); @@ -1136,6 +1142,8 @@ ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, return NULL; } + ep93xx_dma_slave_config_write(chan, dir, &edmac->slave_config); + /* Split the buffer into period size chunks */ first = NULL; for (offset = 0; offset < buf_len; offset += period_len) { @@ -1227,6 +1235,17 @@ static int ep93xx_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *config) { struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); + + memcpy(&edmac->slave_config, config, sizeof(*config)); + + return 0; +} + +static int ep93xx_dma_slave_config_write(struct dma_chan *chan, + enum dma_transfer_direction dir, + struct dma_slave_config *config) +{ + struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); enum dma_slave_buswidth width; unsigned long flags; u32 addr, ctrl; @@ -1234,7 +1253,7 @@ static int ep93xx_dma_slave_config(struct dma_chan *chan, if (!edmac->edma->m2m) return -EINVAL; - switch (config->direction) { + switch (dir) { case DMA_DEV_TO_MEM: width = config->src_addr_width; addr = config->src_addr; -- cgit v1.2.3 From 314448f0386f656aa0f695d8743cc1e8eacde3ac Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 19 Jul 2018 22:22:26 +0530 Subject: dmaengine: mmp_tdma: remove dma_slave_config direction usage dma_slave_config direction was marked as deprecated quite some time back, remove the usage from this driver so that the field can be removed Signed-off-by: Vinod Koul --- drivers/dma/mmp_tdma.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index 13c68b6434ce..c14f8820a567 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -116,6 +116,7 @@ struct mmp_tdma_chan { u32 burst_sz; enum dma_slave_buswidth buswidth; enum dma_status status; + struct dma_slave_config slave_config; int idx; enum mmp_tdma_type type; @@ -139,6 +140,10 @@ struct mmp_tdma_device { #define to_mmp_tdma_chan(dchan) container_of(dchan, struct mmp_tdma_chan, chan) +static int mmp_tdma_config_write(struct dma_chan *chan, + enum dma_transfer_direction dir, + struct dma_slave_config *dmaengine_cfg); + static void mmp_tdma_chan_set_desc(struct mmp_tdma_chan *tdmac, dma_addr_t phys) { writel(phys, tdmac->reg_base + TDNDPR); @@ -442,6 +447,8 @@ static struct dma_async_tx_descriptor *mmp_tdma_prep_dma_cyclic( if (!desc) goto err_out; + mmp_tdma_config_write(chan, direction, &tdmac->slave_config); + while (buf < buf_len) { desc = &tdmac->desc_arr[i]; @@ -495,7 +502,18 @@ static int mmp_tdma_config(struct dma_chan *chan, { struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); - if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { + memcpy(&tdmac->slave_config, dmaengine_cfg, sizeof(*dmaengine_cfg)); + + return 0; +} + +static int mmp_tdma_config_write(struct dma_chan *chan, + enum dma_transfer_direction dir, + struct dma_slave_config *dmaengine_cfg) +{ + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + + if (dir == DMA_DEV_TO_MEM) { tdmac->dev_addr = dmaengine_cfg->src_addr; tdmac->burst_sz = dmaengine_cfg->src_maxburst; tdmac->buswidth = dmaengine_cfg->src_addr_width; @@ -504,7 +522,7 @@ static int mmp_tdma_config(struct dma_chan *chan, tdmac->burst_sz = dmaengine_cfg->dst_maxburst; tdmac->buswidth = dmaengine_cfg->dst_addr_width; } - tdmac->dir = dmaengine_cfg->direction; + tdmac->dir = dir; return mmp_tdma_config_chan(chan); } -- cgit v1.2.3 From 97d49c59e219acac576e16293a6b8cb99302f62f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 14 Sep 2018 17:43:28 +0200 Subject: dmaengine: rcar-dmac: set scatter/gather max segment size Fix warning when running with CONFIG_DMA_API_DEBUG_SG=y by allocating a device_dma_parameters structure and filling in the max segment size. Signed-off-by: Wolfram Sang Signed-off-by: Vinod Koul --- drivers/dma/sh/rcar-dmac.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/dma') diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 48ee35e2bce6..74fa2b1a6a86 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -198,6 +198,7 @@ struct rcar_dmac { struct dma_device engine; struct device *dev; void __iomem *iomem; + struct device_dma_parameters parms; unsigned int n_channels; struct rcar_dmac_chan *channels; @@ -1792,6 +1793,8 @@ static int rcar_dmac_probe(struct platform_device *pdev) dmac->dev = &pdev->dev; platform_set_drvdata(pdev, dmac); + dmac->dev->dma_parms = &dmac->parms; + dma_set_max_seg_size(dmac->dev, RCAR_DMATCR_MASK); dma_set_mask_and_coherent(dmac->dev, DMA_BIT_MASK(40)); ret = rcar_dmac_parse_of(&pdev->dev, dmac); -- cgit v1.2.3 From 0e819e3564c0d79e296d3fc851bb4f78fda22ac2 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Sun, 7 Oct 2018 19:42:56 +0530 Subject: dmaengine: fsl-edma: remove dma_slave_config direction usage dma_slave_config direction was marked as deprecated quite some time back, remove the usage from this driver so that the field can be removed Tested-by: Krzysztof Kozlowski Tested-by: Angelo Dureghello Signed-off-by: Vinod Koul --- drivers/dma/fsl-edma-common.c | 74 +++++++++++++++++++++++++------------------ drivers/dma/fsl-edma-common.h | 12 ++----- 2 files changed, 46 insertions(+), 40 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c index 8ba80f4b6f55..8876c4c1bb2c 100644 --- a/drivers/dma/fsl-edma-common.c +++ b/drivers/dma/fsl-edma-common.c @@ -178,19 +178,7 @@ int fsl_edma_slave_config(struct dma_chan *chan, { struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); - fsl_chan->fsc.dir = cfg->direction; - if (cfg->direction == DMA_DEV_TO_MEM) { - fsl_chan->fsc.dev_addr = cfg->src_addr; - fsl_chan->fsc.addr_width = cfg->src_addr_width; - fsl_chan->fsc.burst = cfg->src_maxburst; - fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width); - } else if (cfg->direction == DMA_MEM_TO_DEV) { - fsl_chan->fsc.dev_addr = cfg->dst_addr; - fsl_chan->fsc.addr_width = cfg->dst_addr_width; - fsl_chan->fsc.burst = cfg->dst_maxburst; - fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width); - } else - return -EINVAL; + memcpy(&fsl_chan->cfg, cfg, sizeof(*cfg)); return 0; } @@ -202,7 +190,7 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan, struct fsl_edma_desc *edesc = fsl_chan->edesc; struct edma_regs *regs = &fsl_chan->edma->regs; u32 ch = fsl_chan->vchan.chan.chan_id; - enum dma_transfer_direction dir = fsl_chan->fsc.dir; + enum dma_transfer_direction dir = edesc->dirn; dma_addr_t cur_addr, dma_addr; size_t len, size; int i; @@ -387,7 +375,7 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( u32 src_addr, dst_addr, last_sg, nbytes; u16 soff, doff, iter; - if (!is_slave_direction(fsl_chan->fsc.dir)) + if (!is_slave_direction(direction)) return NULL; sg_len = buf_len / period_len; @@ -395,9 +383,21 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( if (!fsl_desc) return NULL; fsl_desc->iscyclic = true; + fsl_desc->dirn = direction; dma_buf_next = dma_addr; - nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst; + if (direction == DMA_MEM_TO_DEV) { + fsl_chan->attr = + fsl_edma_get_tcd_attr(fsl_chan->cfg.dst_addr_width); + nbytes = fsl_chan->cfg.dst_addr_width * + fsl_chan->cfg.dst_maxburst; + } else { + fsl_chan->attr = + fsl_edma_get_tcd_attr(fsl_chan->cfg.src_addr_width); + nbytes = fsl_chan->cfg.src_addr_width * + fsl_chan->cfg.src_maxburst; + } + iter = period_len / nbytes; for (i = 0; i < sg_len; i++) { @@ -407,20 +407,20 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( /* get next sg's physical address */ last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; - if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) { + if (direction == DMA_MEM_TO_DEV) { src_addr = dma_buf_next; - dst_addr = fsl_chan->fsc.dev_addr; - soff = fsl_chan->fsc.addr_width; + dst_addr = fsl_chan->cfg.dst_addr; + soff = fsl_chan->cfg.dst_addr_width; doff = 0; } else { - src_addr = fsl_chan->fsc.dev_addr; + src_addr = fsl_chan->cfg.src_addr; dst_addr = dma_buf_next; soff = 0; - doff = fsl_chan->fsc.addr_width; + doff = fsl_chan->cfg.src_addr_width; } fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, dst_addr, - fsl_chan->fsc.attr, soff, nbytes, 0, iter, + fsl_chan->attr, soff, nbytes, 0, iter, iter, doff, last_sg, true, false, true); dma_buf_next += period_len; } @@ -441,42 +441,54 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( u16 soff, doff, iter; int i; - if (!is_slave_direction(fsl_chan->fsc.dir)) + if (!is_slave_direction(direction)) return NULL; fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); if (!fsl_desc) return NULL; fsl_desc->iscyclic = false; + fsl_desc->dirn = direction; + + if (direction == DMA_MEM_TO_DEV) { + fsl_chan->attr = + fsl_edma_get_tcd_attr(fsl_chan->cfg.dst_addr_width); + nbytes = fsl_chan->cfg.dst_addr_width * + fsl_chan->cfg.dst_maxburst; + } else { + fsl_chan->attr = + fsl_edma_get_tcd_attr(fsl_chan->cfg.src_addr_width); + nbytes = fsl_chan->cfg.src_addr_width * + fsl_chan->cfg.src_maxburst; + } - nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst; for_each_sg(sgl, sg, sg_len, i) { /* get next sg's physical address */ last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; - if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) { + if (direction == DMA_MEM_TO_DEV) { src_addr = sg_dma_address(sg); - dst_addr = fsl_chan->fsc.dev_addr; - soff = fsl_chan->fsc.addr_width; + dst_addr = fsl_chan->cfg.dst_addr; + soff = fsl_chan->cfg.dst_addr_width; doff = 0; } else { - src_addr = fsl_chan->fsc.dev_addr; + src_addr = fsl_chan->cfg.src_addr; dst_addr = sg_dma_address(sg); soff = 0; - doff = fsl_chan->fsc.addr_width; + doff = fsl_chan->cfg.src_addr_width; } iter = sg_dma_len(sg) / nbytes; if (i < sg_len - 1) { last_sg = fsl_desc->tcd[(i + 1)].ptcd; fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, - dst_addr, fsl_chan->fsc.attr, soff, + dst_addr, fsl_chan->attr, soff, nbytes, 0, iter, iter, doff, last_sg, false, false, true); } else { last_sg = 0; fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, - dst_addr, fsl_chan->fsc.attr, soff, + dst_addr, fsl_chan->attr, soff, nbytes, 0, iter, iter, doff, last_sg, true, true, false); } diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h index a6f5b99ee95f..8917e8865959 100644 --- a/drivers/dma/fsl-edma-common.h +++ b/drivers/dma/fsl-edma-common.h @@ -109,14 +109,6 @@ struct fsl_edma_sw_tcd { struct fsl_edma_hw_tcd *vtcd; }; -struct fsl_edma_slave_config { - enum dma_transfer_direction dir; - enum dma_slave_buswidth addr_width; - u32 dev_addr; - u32 burst; - u32 attr; -}; - struct fsl_edma_chan { struct virt_dma_chan vchan; enum dma_status status; @@ -125,7 +117,8 @@ struct fsl_edma_chan { u32 slave_id; struct fsl_edma_engine *edma; struct fsl_edma_desc *edesc; - struct fsl_edma_slave_config fsc; + struct dma_slave_config cfg; + u32 attr; struct dma_pool *tcd_pool; }; @@ -133,6 +126,7 @@ struct fsl_edma_desc { struct virt_dma_desc vdesc; struct fsl_edma_chan *echan; bool iscyclic; + enum dma_transfer_direction dirn; unsigned int n_tcds; struct fsl_edma_sw_tcd tcd[]; }; -- cgit v1.2.3 From a3e403161fe1cc87bdbcfb8a4a4eab7aa6ac1a96 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 8 Oct 2018 22:46:31 +0530 Subject: dmaengine: owl: Fix warnings generated during build Following warnings are generated when compiled with W=1, drivers/dma/owl-dma.c:170: warning: Function parameter or member 'cyclic' not described in 'owl_dma_txd' drivers/dma/owl-dma.c:198: warning: Function parameter or member 'cfg' not described in 'owl_dma_vchan' drivers/dma/owl-dma.c:198: warning: Function parameter or member 'drq' not described in 'owl_dma_vchan' drivers/dma/owl-dma.c:225: warning: Function parameter or member 'irq' not described in 'owl_dma' Fix this by adding comments for relevant struct members to appear in kernel-doc. Fixes: d64e1b3f5cce ("dmaengine: owl: Add Slave and Cyclic mode support for Actions Semi Owl S900 SoC") Reported-by: Vinod Koul Signed-off-by: Manivannan Sadhasivam Reviewed-by: Andreas Färber Signed-off-by: Vinod Koul --- drivers/dma/owl-dma.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/dma') diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c index 1d26db4c9229..90bbcef99ef8 100644 --- a/drivers/dma/owl-dma.c +++ b/drivers/dma/owl-dma.c @@ -162,6 +162,7 @@ struct owl_dma_lli { * struct owl_dma_txd - Wrapper for struct dma_async_tx_descriptor * @vd: virtual DMA descriptor * @lli_list: link list of lli nodes + * @cyclic: flag to indicate cyclic transfers */ struct owl_dma_txd { struct virt_dma_desc vd; @@ -188,6 +189,8 @@ struct owl_dma_pchan { * @vc: wrappped virtual channel * @pchan: the physical channel utilized by this channel * @txd: active transaction on this channel + * @cfg: slave configuration for this channel + * @drq: physical DMA request ID for this channel */ struct owl_dma_vchan { struct virt_dma_chan vc; @@ -204,6 +207,7 @@ struct owl_dma_vchan { * @clk: clock for the DMA controller * @lock: a lock to use when change DMA controller global register * @lli_pool: a pool for the LLI descriptors + * @irq: interrupt ID for the DMA controller * @nr_pchans: the number of physical channels * @pchans: array of data for the physical channels * @nr_vchans: the number of physical channels -- cgit v1.2.3 From 27d8d2d7a9b7eb05c4484b74b749eaee7b50b845 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 14 Oct 2018 23:28:50 +0200 Subject: dmaengine: ppc4xx: fix off-by-one build failure There are two poly_store, but one should have been poly_show. |adma.c:4382:16: error: conflicting types for 'poly_store' | static ssize_t poly_store(struct device_driver *dev, const char *buf, | ^~~~~~~~~~ |adma.c:4363:16: note: previous definition of 'poly_store' was here | static ssize_t poly_store(struct device_driver *dev, char *buf) | ^~~~~~~~~~ CC: stable@vger.kernel.org Fixes: 13efe1a05384 ("dmaengine: ppc4xx: remove DRIVER_ATTR() usage") Signed-off-by: Christian Lamparter Signed-off-by: Vinod Koul --- drivers/dma/ppc4xx/adma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index 4cf0d4d0cecf..25610286979f 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -4360,7 +4360,7 @@ static ssize_t enable_store(struct device_driver *dev, const char *buf, } static DRIVER_ATTR_RW(enable); -static ssize_t poly_store(struct device_driver *dev, char *buf) +static ssize_t poly_show(struct device_driver *dev, char *buf) { ssize_t size = 0; u32 reg; -- cgit v1.2.3