Hello, Does anyone have any commnets on this? On Mon, Mar 25, 2019 at 4:15 +0000, Kazuhiro Kasai wrote: > Add Milbeaut AXI DMA controller. This DMA controller has > only capable of memory to memory transfer. > > Signed-off-by: Kazuhiro Kasai <kasai.kazuhiro@xxxxxxxxxxxxx> > --- > drivers/dma/Kconfig | 8 + > drivers/dma/Makefile | 1 + > drivers/dma/xdmac-milbeaut.c | 353 +++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 362 insertions(+) > create mode 100644 drivers/dma/xdmac-milbeaut.c > > diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig > index 0b1dfb5..733fe5f 100644 > --- a/drivers/dma/Kconfig > +++ b/drivers/dma/Kconfig > @@ -612,6 +612,14 @@ config UNIPHIER_MDMAC > UniPhier platform. This DMA controller is used as the external > DMA engine of the SD/eMMC controllers of the LD4, Pro4, sLD8 SoCs. > > +config XDMAC_MILBEAUT > + tristate "Milbeaut AXI DMA support" > + depends on ARCH_MILBEAUT || COMPILE_TEST > + select DMA_ENGINE > + help > + Support for Milbeaut AXI DMA controller driver. The DMA controller > + has only memory to memory capability. > + > config XGENE_DMA > tristate "APM X-Gene DMA support" > depends on ARCH_XGENE || COMPILE_TEST > diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile > index 6126e1c..4aab810 100644 > --- a/drivers/dma/Makefile > +++ b/drivers/dma/Makefile > @@ -72,6 +72,7 @@ obj-$(CONFIG_TEGRA20_APB_DMA) += tegra20-apb-dma.o > obj-$(CONFIG_TEGRA210_ADMA) += tegra210-adma.o > obj-$(CONFIG_TIMB_DMA) += timb_dma.o > obj-$(CONFIG_UNIPHIER_MDMAC) += uniphier-mdmac.o > +obj-$(CONFIG_XDMAC_MILBEAUT) += xdmac-milbeaut.o > obj-$(CONFIG_XGENE_DMA) += xgene-dma.o > obj-$(CONFIG_ZX_DMA) += zx_dma.o > obj-$(CONFIG_ST_FDMA) += st_fdma.o > diff --git a/drivers/dma/xdmac-milbeaut.c b/drivers/dma/xdmac-milbeaut.c > new file mode 100644 > index 0000000..7035c61 > --- /dev/null > +++ b/drivers/dma/xdmac-milbeaut.c > @@ -0,0 +1,353 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2019 Socionext Inc. > + */ > + > +#include <linux/bitfield.h> > +#include <linux/interrupt.h> > +#include <linux/module.h> > +#include <linux/of_dma.h> > +#include <linux/platform_device.h> > + > +#include "dmaengine.h" > + > +/* global register */ > +#define M10V_XDACS 0x00 > + > +/* channel local register */ > +#define M10V_XDTBC 0x10 > +#define M10V_XDSSA 0x14 > +#define M10V_XDDSA 0x18 > +#define M10V_XDSAC 0x1C > +#define M10V_XDDAC 0x20 > +#define M10V_XDDCC 0x24 > +#define M10V_XDDES 0x28 > +#define M10V_XDDPC 0x2C > +#define M10V_XDDSD 0x30 > + > +#define M10V_XDACS_XE BIT(28) > + > +#define M10V_XDSAC_SBS GENMASK(17, 16) > +#define M10V_XDSAC_SBL GENMASK(11, 8) > + > +#define M10V_XDDAC_DBS GENMASK(17, 16) > +#define M10V_XDDAC_DBL GENMASK(11, 8) > + > +#define M10V_XDDES_CE BIT(28) > +#define M10V_XDDES_SE BIT(24) > +#define M10V_XDDES_SA BIT(15) > +#define M10V_XDDES_TF GENMASK(23, 20) > +#define M10V_XDDES_EI BIT(1) > +#define M10V_XDDES_TI BIT(0) > + > +#define M10V_XDDSD_IS_MASK GENMASK(3, 0) > +#define M10V_XDDSD_IS_NORMAL 0x8 > + > +#define M10V_XDMAC_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)) > + > +#define M10V_XDMAC_CHAN_BASE(base, i) ((base) + (i) * 0x30) > + > +#define to_m10v_dma_chan(c) container_of((c), struct m10v_dma_chan, chan) > + > +struct m10v_dma_desc { > + struct dma_async_tx_descriptor txd; > + size_t len; > + dma_addr_t src; > + dma_addr_t dst; > +}; > + > +struct m10v_dma_chan { > + struct dma_chan chan; > + struct m10v_dma_device *mdmac; > + void __iomem *regs; > + int irq; > + struct m10v_dma_desc mdesc; > + spinlock_t lock; > +}; > + > +struct m10v_dma_device { > + struct dma_device dmac; > + void __iomem *regs; > + unsigned int channels; > + struct m10v_dma_chan mchan[0]; > +}; > + > +static void m10v_xdmac_enable_dma(struct m10v_dma_device *mdmac) > +{ > + unsigned int val; > + > + val = readl(mdmac->regs + M10V_XDACS); > + val &= ~M10V_XDACS_XE; > + val |= FIELD_PREP(M10V_XDACS_XE, 1); > + writel(val, mdmac->regs + M10V_XDACS); > +} > + > +static void m10v_xdmac_disable_dma(struct m10v_dma_device *mdmac) > +{ > + unsigned int val; > + > + val = readl(mdmac->regs + M10V_XDACS); > + val &= ~M10V_XDACS_XE; > + val |= FIELD_PREP(M10V_XDACS_XE, 0); > + writel(val, mdmac->regs + M10V_XDACS); > +} > + > +static void m10v_xdmac_config_chan(struct m10v_dma_chan *mchan) > +{ > + u32 val; > + > + val = mchan->mdesc.len - 1; > + writel(val, mchan->regs + M10V_XDTBC); > + > + val = mchan->mdesc.src; > + writel(val, mchan->regs + M10V_XDSSA); > + > + val = mchan->mdesc.dst; > + writel(val, mchan->regs + M10V_XDDSA); > + > + val = readl(mchan->regs + M10V_XDSAC); > + val &= ~(M10V_XDSAC_SBS | M10V_XDSAC_SBL); > + val |= FIELD_PREP(M10V_XDSAC_SBS, 0x3) | > + FIELD_PREP(M10V_XDSAC_SBL, 0xf); > + writel(val, mchan->regs + M10V_XDSAC); > + > + val = readl(mchan->regs + M10V_XDDAC); > + val &= ~(M10V_XDDAC_DBS | M10V_XDDAC_DBL); > + val |= FIELD_PREP(M10V_XDDAC_DBS, 0x3) | > + FIELD_PREP(M10V_XDDAC_DBL, 0xf); > + writel(val, mchan->regs + M10V_XDDAC); > +} > + > +static void m10v_xdmac_enable_chan(struct m10v_dma_chan *mchan) > +{ > + u32 val; > + > + val = readl(mchan->regs + M10V_XDDES); > + val &= ~(M10V_XDDES_CE | > + M10V_XDDES_SE | > + M10V_XDDES_TF | > + M10V_XDDES_EI | > + M10V_XDDES_TI); > + val |= FIELD_PREP(M10V_XDDES_CE, 1) | > + FIELD_PREP(M10V_XDDES_SE, 1) | > + FIELD_PREP(M10V_XDDES_TF, 1) | > + FIELD_PREP(M10V_XDDES_EI, 1) | > + FIELD_PREP(M10V_XDDES_TI, 1); > + writel(val, mchan->regs + M10V_XDDES); > +} > + > +static void m10v_xdmac_disable_chan(struct m10v_dma_chan *mchan) > +{ > + u32 val; > + > + val = readl(mchan->regs + M10V_XDDES); > + val &= ~M10V_XDDES_CE; > + val |= FIELD_PREP(M10V_XDDES_CE, 0); > + writel(val, mchan->regs + M10V_XDDES); > +} > + > +static irqreturn_t m10v_xdmac_irq(int irq, void *data) > +{ > + struct m10v_dma_chan *mchan = data; > + unsigned long flags; > + u32 val; > + > + val = readl(mchan->regs + M10V_XDDSD); > + val = FIELD_GET(M10V_XDDSD_IS_MASK, val); > + > + if (val != M10V_XDDSD_IS_NORMAL) > + dev_err(mchan->chan.device->dev, "XDMAC error with status: %x", val); > + > + val = FIELD_PREP(M10V_XDDSD_IS_MASK, 0x0); > + writel(val, mchan->regs + M10V_XDDSD); > + > + spin_lock_irqsave(&mchan->lock, flags); > + dma_cookie_complete(&mchan->mdesc.txd); > + spin_unlock_irqrestore(&mchan->lock, flags); > + > + if (mchan->mdesc.txd.flags & DMA_PREP_INTERRUPT) > + dmaengine_desc_get_callback_invoke(&mchan->mdesc.txd, NULL); > + > + return IRQ_HANDLED; > +} > + > +static void m10v_xdmac_issue_pending(struct dma_chan *chan) > +{ > + struct m10v_dma_chan *mchan = to_m10v_dma_chan(chan); > + > + m10v_xdmac_config_chan(mchan); > + > + m10v_xdmac_enable_chan(mchan); > +} > + > +static dma_cookie_t m10v_xdmac_tx_submit(struct dma_async_tx_descriptor *txd) > +{ > + struct m10v_dma_chan *mchan = to_m10v_dma_chan(txd->chan); > + dma_cookie_t cookie; > + unsigned long flags; > + > + spin_lock_irqsave(&mchan->lock, flags); > + cookie = dma_cookie_assign(txd); > + spin_unlock_irqrestore(&mchan->lock, flags); > + > + return cookie; > +} > + > +static struct dma_async_tx_descriptor * > +m10v_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, > + dma_addr_t src, size_t len, unsigned long flags) > +{ > + struct m10v_dma_chan *mchan = to_m10v_dma_chan(chan); > + > + dma_async_tx_descriptor_init(&mchan->mdesc.txd, chan); > + mchan->mdesc.txd.tx_submit = m10v_xdmac_tx_submit; > + mchan->mdesc.txd.callback = NULL; > + mchan->mdesc.txd.flags = flags; > + mchan->mdesc.txd.cookie = -EBUSY; > + > + mchan->mdesc.len = len; > + mchan->mdesc.src = src; > + mchan->mdesc.dst = dst; > + > + return &mchan->mdesc.txd; > +} > + > +static int m10v_xdmac_device_terminate_all(struct dma_chan *chan) > +{ > + struct m10v_dma_chan *mchan = to_m10v_dma_chan(chan); > + > + m10v_xdmac_disable_chan(mchan); > + > + return 0; > +} > + > +static int m10v_xdmac_alloc_chan_resources(struct dma_chan *chan) > +{ > + struct m10v_dma_chan *mchan = to_m10v_dma_chan(chan); > + unsigned long flags; > + > + spin_lock_irqsave(&mchan->lock, flags); > + dma_cookie_init(chan); > + spin_unlock_irqrestore(&mchan->lock, flags); > + > + return 1; > +} > + > +static int m10v_xdmac_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + struct m10v_dma_chan *mchan; > + struct m10v_dma_device *mdmac; > + struct resource *res; > + unsigned int channels; > + int ret, i; > + > + ret = device_property_read_u32(&pdev->dev, "dma-channels", &channels); > + if (ret) { > + dev_err(&pdev->dev, "get dma-channels failed\n"); > + return ret; > + } > + > + mdmac = devm_kzalloc(&pdev->dev, > + struct_size(mdmac, mchan, channels), > + GFP_KERNEL); > + if (!mdmac) > + return -ENOMEM; > + > + mdmac->channels = channels; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + mdmac->regs = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(mdmac->regs)) > + return PTR_ERR(mdmac->regs); > + > + INIT_LIST_HEAD(&mdmac->dmac.channels); > + for (i = 0; i < mdmac->channels; i++) { > + mchan = &mdmac->mchan[i]; > + mchan->irq = platform_get_irq(pdev, i); > + ret = devm_request_irq(&pdev->dev, mchan->irq, m10v_xdmac_irq, > + IRQF_SHARED, dev_name(&pdev->dev), mchan); > + if (ret) { > + dev_err(&pdev->dev, "failed to request IRQ\n"); > + return ret; > + } > + mchan->mdmac = mdmac; > + mchan->chan.device = &mdmac->dmac; > + list_add_tail(&mchan->chan.device_node, > + &mdmac->dmac.channels); > + > + mchan->regs = M10V_XDMAC_CHAN_BASE(mdmac->regs, i); > + spin_lock_init(&mchan->lock); > + } > + > + dma_cap_set(DMA_MEMCPY, mdmac->dmac.cap_mask); > + > + mdmac->dmac.device_alloc_chan_resources = m10v_xdmac_alloc_chan_resources; > + mdmac->dmac.device_prep_dma_memcpy = m10v_xdmac_prep_dma_memcpy; > + mdmac->dmac.device_issue_pending = m10v_xdmac_issue_pending; > + mdmac->dmac.device_tx_status = dma_cookie_status; > + mdmac->dmac.device_terminate_all = m10v_xdmac_device_terminate_all; > + mdmac->dmac.src_addr_widths = M10V_XDMAC_BUSWIDTHS; > + mdmac->dmac.dst_addr_widths = M10V_XDMAC_BUSWIDTHS; > + mdmac->dmac.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; > + mdmac->dmac.dev = &pdev->dev; > + > + platform_set_drvdata(pdev, mdmac); > + > + m10v_xdmac_enable_dma(mdmac); > + > + ret = dmaenginem_async_device_register(&mdmac->dmac); > + if (ret) { > + dev_err(&pdev->dev, "failed to register dmaengine device\n"); > + return ret; > + } > + > + ret = of_dma_controller_register(np, of_dma_simple_xlate, mdmac); > + if (ret) { > + dev_err(&pdev->dev, "failed to register OF DMA controller\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int m10v_xdmac_remove(struct platform_device *pdev) > +{ > + struct m10v_dma_chan *mchan; > + struct m10v_dma_device *mdmac = platform_get_drvdata(pdev); > + int i; > + > + m10v_xdmac_disable_dma(mdmac); > + > + for (i = 0; i < mdmac->channels; i++) { > + mchan = &mdmac->mchan[i]; > + devm_free_irq(&pdev->dev, mchan->irq, mchan); > + } > + > + of_dma_controller_free(pdev->dev.of_node); > + > + return 0; > +} > + > +static const struct of_device_id m10v_xdmac_dt_ids[] = { > + {.compatible = "socionext,milbeaut-m10v-xdmac",}, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, m10v_xdmac_dt_ids); > + > +static struct platform_driver m10v_xdmac_driver = { > + .driver = { > + .name = "m10v-xdmac", > + .of_match_table = of_match_ptr(m10v_xdmac_dt_ids), > + }, > + .probe = m10v_xdmac_probe, > + .remove = m10v_xdmac_remove, > +}; > +module_platform_driver(m10v_xdmac_driver); > + > +MODULE_AUTHOR("Kazuhiro Kasai <kasai.kazuhiro@xxxxxxxxxxxxx>"); > +MODULE_DESCRIPTION("Socionext Milbeaut XDMAC driver"); > +MODULE_LICENSE("GPL v2"); > -- > 1.9.1 >