Hi Krzysztof, On Mon, Aug 06, 2018 at 10:09:08AM +0200, Krzysztof Kozlowski wrote: > On 3 August 2018 at 21:32, Angelo Dureghello <angelo@xxxxxxxx> wrote: > > 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 <angelo@xxxxxxxx> > > --- > > Changes for v7: > > - patch rewritten from scratch, this patch (3/3) has just been added. > > --- > > drivers/dma/Kconfig | 11 + > > drivers/dma/Makefile | 1 + > > drivers/dma/fsl-edma-common.c | 24 +- > > drivers/dma/mcf-edma.c | 315 +++++++++++++++++++++ > > include/linux/platform_data/dma-mcf-edma.h | 38 +++ > > 5 files changed, 385 insertions(+), 4 deletions(-) > > create mode 100644 drivers/dma/mcf-edma.c > > create mode 100644 include/linux/platform_data/dma-mcf-edma.h > > > > diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig > > index ca1680afa20a..b45008e9c7e9 100644 > > --- a/drivers/dma/Kconfig > > +++ b/drivers/dma/Kconfig > > @@ -320,6 +320,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 > > + 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 66022f59fca4..d97f317f4b34 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 948a3ee51bbb..5a830a238a0c 100644 > > --- a/drivers/dma/fsl-edma-common.c > > +++ b/drivers/dma/fsl-edma-common.c > > @@ -102,8 +102,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) > > @@ -111,8 +119,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..31e5317a8f90 > > --- /dev/null > > +++ b/drivers/dma/mcf-edma.c > > @@ -0,0 +1,315 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +// > > +// Copyright (c) 2013-2014 Freescale Semiconductor, Inc > > Same comment as to 1/3 - if this is derivative work... > Well, in this case the driver is brand new, i maintained the Freescale Copyright too, since i copied some code from fsl-edma.c. > > +// Copyright (c) 2017 Sysam, Angelo Dureghello <angelo@xxxxxxxx> > > + > > +#include <linux/module.h> > > +#include <linux/interrupt.h> > > +#include <linux/dmaengine.h> > > +#include <linux/platform_device.h> > > +#include <linux/platform_data/dma-mcf-edma.h> > > + > > +#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) > > + return PTR_ERR(pdata); > > + > > + 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 == (int)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..9a1819acb28f > > --- /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 <angelo@xxxxxxxx> > > + * > > + * 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 __MACH_MCF_EDMA_H__ > > +#define __MACH_MCF_EDMA_H__ > > The guard should rather match current location, e.g. > __LINUX_PLATFORM_DATA_MCF_EDMA_H__ > Ack. > Tested-by: Krzysztof Kozlowski <krzk@xxxxxxxxxx> > > Best regards, > Krzysztof > -- > To unsubscribe from this list: send the line "unsubscribe dmaengine" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html Regards, Angelo -- To unsubscribe from this list: send the line "unsubscribe dmaengine" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html