[PATCH v3] dmaengine: add CSR SiRFprimaII DMAC driver
Vinod Koul
vinod.koul at intel.com
Mon Oct 17 09:57:41 EDT 2011
On Mon, 2011-10-17 at 02:36 -0700, Barry Song wrote:
> From: Rongjun Ying <Rongjun.Ying at csr.com>
>
> Cc: Jassi Brar <jaswinder.singh at linaro.org>
> Cc: Arnd Bergmann <arnd at arndb.de>
> Cc: Linus Walleij <linus.walleij at linaro.org>
> Signed-off-by: Rongjun Ying <rongjun.ying at csr.com>
> Signed-off-by: Barry Song <Barry.Song at csr.com>
> ---
> -v3:
> use new dma_transfer_direction API from Jassi and Vinod;
> use new dma_interleaved_template API from Jassi Brar;
> fix comment minor issues from Arnd and Vinod in v2;
> add prima2 loop DMA mode support.
>
> MAINTAINERS | 1 +
> drivers/dma/Kconfig | 7 +
> drivers/dma/Makefile | 1 +
> drivers/dma/sirf-dma.c | 685 ++++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 694 insertions(+), 0 deletions(-)
> create mode 100644 drivers/dma/sirf-dma.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5483e0c..adcb6ce 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -739,6 +739,7 @@ M: Barry Song <baohua.song at csr.com>
> L: linux-arm-kernel at lists.infradead.org (moderated for non-subscribers)
> S: Maintained
> F: arch/arm/mach-prima2/
> +F: drivers/dma/sirf-dma*
>
> ARM/EBSA110 MACHINE SUPPORT
> M: Russell King <linux at arm.linux.org.uk>
> +++ b/drivers/dma/sirf-dma.c
> @@ -0,0 +1,685 @@
> +/*
> + * DMA controller driver for CSR SiRFprimaII
> + *
> + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
> + *
> + * Licensed under GPLv2 or later.
There is a mismatch between this and MODULE_LICENSE()
> +
> +
> +struct sirfsoc_dma_chan {
> + struct dma_chan chan;
> + struct list_head free;
> + struct list_head prepared;
> + struct list_head queued;
> + struct list_head active;
> + struct list_head completed;
> + dma_cookie_t completed_cookie;
> + unsigned long happened_cyclic;
> + unsigned long completed_cyclic;
> +
> + /* Lock for this structure */
> + spinlock_t lock;
> +
> + int mode;
alignment pls...
> +
> +/* Execute all queued DMA descriptors */
> +static void sirfsoc_dma_execute(struct sirfsoc_dma_chan *schan)
> +{
> + struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan);
> + int cid = schan->chan.chan_id;
> + struct sirfsoc_dma_desc *sdesc = NULL;
> +
> + sdesc = list_first_entry(&schan->queued, struct sirfsoc_dma_desc,
> + node);
> + /* Move the first queued descriptor to active list */
> + list_move_tail(&schan->queued, &schan->active);
shouldn't lock be held before this, and if this function is called with
lock held, would help to mention that explicitly
> +
> + /* Start the DMA transfer */
> + writel_relaxed(sdesc->width, sdma->base + SIRFSOC_DMA_WIDTH_0 + cid * 4);
> + writel_relaxed(cid | (schan->mode << SIRFSOC_DMA_MODE_CTRL_BIT) |
> + (sdesc->dir << SIRFSOC_DMA_DIR_CTRL_BIT),
> + sdma->base + cid * 0x10 + SIRFSOC_DMA_CH_CTRL);
> + writel_relaxed(sdesc->xlen, sdma->base + cid * 0x10 + SIRFSOC_DMA_CH_XLEN);
> + writel_relaxed(sdesc->ylen, sdma->base + cid * 0x10 + SIRFSOC_DMA_CH_YLEN);
> + writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) | (1 << cid),
> + sdma->base + SIRFSOC_DMA_INT_EN);
> + writel(sdesc->addr >> 2, sdma->base + cid * 0x10 + SIRFSOC_DMA_CH_ADDR);
> +
> + if (sdesc->cyclic) {
> + writel((1 << cid) | 1 << (cid + 16) |
> + readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL),
> + sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
> + schan->happened_cyclic = schan->completed_cyclic = 0;
> + }
any reason why we have mixed use of writel_relaxes and writel?
Shouldn't all the DMA register writes be done only using writel?
> +}
> +
> +
> +static struct dma_async_tx_descriptor *sirfsoc_dma_prep_interleaved(
> + struct dma_chan *chan, struct dma_interleaved_template *xt)
> +{
> + struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(chan);
> + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan);
> + struct sirfsoc_dma_desc *sdesc = NULL;
> + unsigned long iflags;
> + int ret;
> +
> + if ((xt->dir != MEM_TO_DEV) || (xt->dir != DEV_TO_MEM)) {
> + ret = -EINVAL;
> + goto err_dir;
> + }
> +
> + /* Get free descriptor */
> + spin_lock_irqsave(&schan->lock, iflags);
> + if (!list_empty(&schan->free)) {
> + sdesc = list_first_entry(&schan->free, struct sirfsoc_dma_desc,
> + node);
> + list_del(&sdesc->node);
> + }
> + spin_unlock_irqrestore(&schan->lock, iflags);
> +
> + if (!sdesc) {
> + /* try to free completed descriptors */
> + sirfsoc_dma_process_completed(sdma);
> + ret = 0;
> + goto no_desc;
> + }
> +
> + /* Place descriptor in prepared list */
> + spin_lock_irqsave(&schan->lock, iflags);
> + if ((xt->frame_size == 1) && (xt->numf > 0)) {
what does this mean?
> + sdesc->cyclic = 0;
> + sdesc->xlen = xt->sgl[0].size / 4;
> + sdesc->width = (xt->sgl[0].size + xt->sgl[0].icg) / 4;
whats so magical about 4?
> + sdesc->ylen = xt->numf - 1;
> + if (xt->dir == MEM_TO_DEV) {
> + sdesc->addr = xt->src_start;
> + sdesc->dir = 1;
> + } else {
> + sdesc->addr = xt->dst_start;
> + sdesc->dir = 0;
> + }
> +
> + list_add_tail(&sdesc->node, &schan->prepared);
> + } else {
> + pr_err("sirfsoc DMA Invalid xfer\n");
> + ret = -EINVAL;
> + goto err_xfer;
> + }
> + spin_unlock_irqrestore(&schan->lock, iflags);
> +
> + return &sdesc->desc;
> +err_xfer:
> + spin_unlock_irqrestore(&schan->lock, iflags);
> +no_desc:
> +err_dir:
> + return ERR_PTR(ret);
> +}
> +
> +static struct dma_async_tx_descriptor *
> +sirfsoc_dma_prep_cyclic(struct dma_chan *chan, dma_addr_t addr,
> + size_t buf_len, size_t period_len,
> + enum dma_transfer_direction direction)
> +{
> + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan);
> + struct sirfsoc_dma_desc *sdesc = NULL;
> + unsigned long iflags;
> +
> + /*
> + * we only support cycle transfer with 2 period
> + * If the X-length is set to 0, it would be the loop mode.
> + * The DMA address keeps increasing until reaching the end of a loop
> + * area whose size is defined by (DMA_WIDTH x (Y_LENGTH + 1)). Then
> + * the DMA address goes back to the beginning of this area.
> + * In loop mode, the DMA data region is divided into two parts, BUFA
> + * and BUFB. DMA controller generates interrupts twice in each loop:
> + * when the DMA address reaches the end of BUFA or the end of the
> + * BUFB
> + */
> + if (buf_len != 2 * period_len)
> + return ERR_PTR(-EINVAL);
> +
> + /* Get free descriptor */
> + spin_lock_irqsave(&schan->lock, iflags);
> + if (!list_empty(&schan->free)) {
> + sdesc = list_first_entry(&schan->free, struct sirfsoc_dma_desc,
> + node);
> + list_del(&sdesc->node);
> + }
> + spin_unlock_irqrestore(&schan->lock, iflags);
> +
> + if (!sdesc)
> + return 0;
> +
> + /* Place descriptor in prepared list */
> + spin_lock_irqsave(&schan->lock, iflags);
> + sdesc->addr = addr;
> + sdesc->cyclic = 1;
> + sdesc->xlen = 0;
> + sdesc->ylen = buf_len / 4 - 1;
> + sdesc->width = 1;
> + list_add_tail(&sdesc->node, &schan->prepared);
> + spin_unlock_irqrestore(&schan->lock, iflags);
> +
> + return &sdesc->desc;
> +}
is this interleave cyclic dma or plain sg?
> +
> +/*
> + * The DMA controller consists of 16 independent DMA channels.
> + * Each channel is allocated to a different function
> + */
> +bool sirfsoc_dma_filter_id(struct dma_chan *chan, void *chan_id)
> +{
> + unsigned int ch_nr = (unsigned int) chan_id;
> +
> + if (ch_nr == chan->chan_id +
> + chan->device->dev_id * SIRFSOC_DMA_CHANNELS)
> + return true;
pls fix indent here
> +
> + return false;
> +}
> +EXPORT_SYMBOL(sirfsoc_dma_filter_id);
> +
> +static int __devinit sirfsoc_dma_probe(struct platform_device *op)
> +{
> + struct device_node *dn = op->dev.of_node;
> + struct device *dev = &op->dev;
> + struct dma_device *dma;
> + struct sirfsoc_dma *sdma;
> + struct sirfsoc_dma_chan *schan;
> + struct resource res;
> + ulong regs_start, regs_size;
> + u32 id;
> + int retval, i;
> +
> + sdma = devm_kzalloc(dev, sizeof(*sdma), GFP_KERNEL);
> + if (!sdma) {
> + dev_err(dev, "Memory exhausted!\n");
> + return -ENOMEM;
> + }
> +
> + if (of_property_read_u32(dn, "cell-index", &id)) {
> + dev_err(dev, "Fail to get DMAC index\n");
and you leak memory!!
> + return -ENODEV;
> + }
> +
> + sdma->irq = irq_of_parse_and_map(dn, 0);
> + if (sdma->irq == NO_IRQ) {
> + dev_err(dev, "Error mapping IRQ!\n");
and again...
> + return -EINVAL;
> + }
> +
> + retval = of_address_to_resource(dn, 0, &res);
> + if (retval) {
> + dev_err(dev, "Error parsing memory region!\n");
and again...
> + return retval;
> + }
> +
> + regs_start = res.start;
> + regs_size = resource_size(&res);
> +
> + if (!devm_request_mem_region(dev, regs_start, regs_size, DRV_NAME)) {
> + dev_err(dev, "Error requesting memory region!\n");
> + return -EBUSY;
> + }
> +
> + sdma->base = devm_ioremap(dev, regs_start, regs_size);
> + if (!sdma->base) {
> + dev_err(dev, "Error mapping memory region!\n");
> + return -ENOMEM;
> + }
> +
> + retval = devm_request_irq(dev, sdma->irq, &sirfsoc_dma_irq, 0, DRV_NAME,
> + sdma);
> + if (retval) {
> + dev_err(dev, "Error requesting IRQ!\n");
> + return -EINVAL;
> + }
> +
> + dma = &sdma->dma;
> + dma->dev = dev;
> + dma->chancnt = SIRFSOC_DMA_CHANNELS;
> +
> + dma->device_alloc_chan_resources = sirfsoc_dma_alloc_chan_resources;
> + dma->device_free_chan_resources = sirfsoc_dma_free_chan_resources;
> + dma->device_issue_pending = sirfsoc_dma_issue_pending;
> + dma->device_control = sirfsoc_dma_control;
> + dma->device_tx_status = sirfsoc_dma_tx_status;
> + dma->device_prep_interleaved_dma = sirfsoc_dma_prep_interleaved;
> + dma->device_prep_dma_cyclic = sirfsoc_dma_prep_cyclic;
> +
> + INIT_LIST_HEAD(&dma->channels);
> + dma_cap_set(DMA_SLAVE, dma->cap_mask);
> + dma_cap_set(DMA_CYCLIC, dma->cap_mask);
> + dma_cap_set(DMA_INTERLEAVE, dma->cap_mask);
> + dma_cap_set(DMA_PRIVATE, dma->cap_mask);
> +
> + for (i = 0; i < dma->chancnt; i++) {
> + schan = &sdma->channels[i];
> +
> + schan->chan.device = dma;
> + schan->chan.cookie = 1;
> + schan->completed_cookie = schan->chan.cookie;
> +
> + INIT_LIST_HEAD(&schan->free);
> + INIT_LIST_HEAD(&schan->prepared);
> + INIT_LIST_HEAD(&schan->queued);
> + INIT_LIST_HEAD(&schan->active);
> + INIT_LIST_HEAD(&schan->completed);
> +
> + spin_lock_init(&schan->lock);
> + list_add_tail(&schan->chan.device_node, &dma->channels);
> + }
> +
> + tasklet_init(&sdma->tasklet, sirfsoc_dma_tasklet, (unsigned long)sdma);
> +
> + /* Register DMA engine */
> + dev_set_drvdata(dev, sdma);
> + retval = dma_async_device_register(dma);
> + if (retval) {
> + devm_free_irq(dev, sdma->irq, sdma);
> + irq_dispose_mapping(sdma->irq);
> + }
> +
> + return retval;
> +}
the error path handling in this function is totally non existent!!
> +
> +static int __devexit sirfsoc_dma_remove(struct platform_device *op)
> +{
> + struct device *dev = &op->dev;
> + struct sirfsoc_dma *sdma = dev_get_drvdata(dev);
> +
> + dma_async_device_unregister(&sdma->dma);
> + devm_free_irq(dev, sdma->irq, sdma);
> + irq_dispose_mapping(sdma->irq);
who will free mem allocated in probe?
> +
> + return 0;
> +}
> +
--
~Vinod
More information about the linux-arm-kernel
mailing list