[PATCH v2 1/2] dmaengine: add Qualcomm BAM dma driver
Andy Gross
agross at codeaurora.org
Mon Jan 20 18:31:54 EST 2014
On Mon, Jan 13, 2014 at 10:31:01AM +0000, Shevchenko, Andriy wrote:
> On Fri, 2014-01-10 at 13:07 -0600, Andy Gross wrote:
> > Add the DMA engine driver for the QCOM Bus Access Manager (BAM) DMA controller
> > found in the MSM 8x74 platforms.
> >
> > Each BAM DMA device is associated with a specific on-chip peripheral. Each
> > channel provides a uni-directional data transfer engine that is capable of
> > transferring data between the peripheral and system memory (System mode), or
> > between two peripherals (BAM2BAM).
> >
> > The initial release of this driver only supports slave transfers between
> > peripherals and system memory.
> >
> > Signed-off-by: Andy Gross <agross at codeaurora.org>
> > ---
> > drivers/dma/Kconfig | 9 +
> > drivers/dma/Makefile | 1 +
> > drivers/dma/qcom_bam_dma.c | 843 +++++++++++++++++++++++++++++++++++++++++++++
> > drivers/dma/qcom_bam_dma.h | 268 ++++++++++++++
> > 4 files changed, 1121 insertions(+)
> > create mode 100644 drivers/dma/qcom_bam_dma.c
> > create mode 100644 drivers/dma/qcom_bam_dma.h
> >
[...]
> > + * bam_tx_status - returns status of transaction
> > + * @chan: dma channel
> > + * @cookie: transaction cookie
> > + * @txstate: DMA transaction state
> > + *
> > + * Return status of dma transaction
> > + */
> > +static enum dma_status bam_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
> > + struct dma_tx_state *txstate)
> > +{
> > + struct bam_chan *bchan = to_bam_chan(chan);
> > + struct virt_dma_desc *vd;
> > + int ret;
> > + size_t residue = 0;
> > + unsigned int i;
> > + unsigned long flags;
> > +
> > + ret = dma_cookie_status(chan, cookie, txstate);
> > +
>
> Redundant empty line.
>
Will remove.
> > + if (ret == DMA_COMPLETE)
> > + return ret;
> > +
> > + if (!txstate)
> > + return bchan->paused ? DMA_PAUSED : ret;
> > +
> > + spin_lock_irqsave(&bchan->vc.lock, flags);
> > + vd = vchan_find_desc(&bchan->vc, cookie);
> > + if (vd)
> > + residue = container_of(vd, struct bam_async_desc, vd)->length;
> > + else if (bchan->curr_txd && bchan->curr_txd->vd.tx.cookie == cookie)
> > + for (i = 0; i < bchan->curr_txd->num_desc; i++)
> > + residue += bchan->curr_txd->curr_desc[i].size;
> > +
> > + dma_set_residue(txstate, residue);
>
> I'm pretty sure you could do this outside of spin lock.
>
Yes, I'll move it.
> > +
> > + spin_unlock_irqrestore(&bchan->vc.lock, flags);
> > +
> > + if (ret == DMA_IN_PROGRESS && bchan->paused)
> > + ret = DMA_PAUSED;
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * bam_start_dma - start next transaction
> > + * @bchan - bam dma channel
> > + *
> > + * Note: must hold bam dma channel vc.lock
> > + */
> > +static void bam_start_dma(struct bam_chan *bchan)
> > +{
> > + struct virt_dma_desc *vd = vchan_next_desc(&bchan->vc);
> > + struct bam_device *bdev = bchan->bdev;
> > + struct bam_async_desc *async_desc;
> > + struct bam_desc_hw *desc;
> > + struct bam_desc_hw *fifo = PTR_ALIGN(bchan->fifo_virt,
> > + sizeof(struct bam_desc_hw));
>
> > +
> > + if (!vd)
> > + return;
> > +
> > + list_del(&vd->node);
> > +
> > + async_desc = container_of(vd, struct bam_async_desc, vd);
> > + bchan->curr_txd = async_desc;
> > +
> > + desc = bchan->curr_txd->curr_desc;
> > +
> > + if (async_desc->num_desc > MAX_DESCRIPTORS)
> > + async_desc->xfer_len = MAX_DESCRIPTORS;
> > + else
> > + async_desc->xfer_len = async_desc->num_desc;
> > +
> > + /* set INT on last descriptor */
> > + desc[async_desc->xfer_len - 1].flags |= DESC_FLAG_INT;
> > +
> > + if (bchan->tail + async_desc->xfer_len > MAX_DESCRIPTORS) {
> > + u32 partial = MAX_DESCRIPTORS - bchan->tail;
> > +
> > + memcpy(&fifo[bchan->tail], desc,
> > + partial * sizeof(struct bam_desc_hw));
> > + memcpy(fifo, &desc[partial], (async_desc->xfer_len - partial) *
> > + sizeof(struct bam_desc_hw));
>
> I'm just curious if you could avoid memcpys at all somehow.
>
Unfortunately not. The descriptors have to be copied into the FIFO memory that
is being used by the dma controller for this channel. Due to the way the FIFO
works, I have to copy into the FIFO during either the issue_pending or when I
start the next transaction. Either way, it means copying from the txd to the
FIFO.
> > + } else
>
> Keep style
>
OK.
> } else {
> ...
> }
>
> Have you run checkpatch.pl?
>
Yes. And I fixed any discrepancies before sending this.
> > + memcpy(&fifo[bchan->tail], desc,
> > + async_desc->xfer_len * sizeof(struct bam_desc_hw));
> > +
> > + bchan->tail += async_desc->xfer_len;
> > + bchan->tail %= MAX_DESCRIPTORS;
> > +
> > + /* ensure descriptor writes and dma start not reordered */
> > + wmb();
> > + writel_relaxed(bchan->tail * sizeof(struct bam_desc_hw),
> > + bdev->regs + BAM_P_EVNT_REG(bchan->id));
> > +}
> > +
> > +/**
> > + * dma_tasklet - DMA IRQ tasklet
> > + * @data: tasklet argument (bam controller structure)
> > + *
> > + * Sets up next DMA operation and then processes all completed transactions
> > + */
> > +static void dma_tasklet(unsigned long data)
> > +{
> > + struct bam_device *bdev = (struct bam_device *)data;
> > + struct bam_chan *bchan;
> > + unsigned long flags;
> > + unsigned int i;
> > +
> > + /* go through the channels and kick off transactions */
> > + for (i = 0; i < bdev->num_channels; i++) {
> > + bchan = &bdev->channels[i];
> > + spin_lock_irqsave(&bchan->vc.lock, flags);
> > +
> > + if (!list_empty(&bchan->vc.desc_issued) && !bchan->curr_txd)
> > + bam_start_dma(bchan);
> > + spin_unlock_irqrestore(&bchan->vc.lock, flags);
> > + }
> > +}
> > +
> > +/**
> > + * bam_issue_pending - starts pending transactions
> > + * @chan: dma channel
> > + *
> > + * Calls tasklet directly which in turn starts any pending transactions
> > + */
> > +static void bam_issue_pending(struct dma_chan *chan)
> > +{
> > + struct bam_chan *bchan = to_bam_chan(chan);
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(&bchan->vc.lock, flags);
> > +
> > + /* if work pending and idle, start a transaction */
> > + if (vchan_issue_pending(&bchan->vc) && !bchan->curr_txd)
> > + bam_start_dma(bchan);
> > +
> > + spin_unlock_irqrestore(&bchan->vc.lock, flags);
> > +}
> > +
> > +/**
> > + * bam_dma_free_desc - free descriptor memory
> > + * @vd: virtual descriptor
> > + *
> > + */
> > +static void bam_dma_free_desc(struct virt_dma_desc *vd)
> > +{
> > + struct bam_async_desc *async_desc = container_of(vd,
> > + struct bam_async_desc, vd);
> > +
> > + kfree(async_desc);
> > +}
> > +
> > +struct bam_filter_args {
> > + struct dma_device *dev;
> > + u32 id;
> > + u32 ee;
> > + u32 dir;
> > +};
> > +
> > +static bool bam_dma_filter(struct dma_chan *chan, void *data)
> > +{
> > + struct bam_filter_args *args = data;
> > + struct bam_chan *bchan = to_bam_chan(chan);
> > +
> > + if (args->dev == chan->device &&
> > + args->id == bchan->id) {
> > +
> > + /* we found the channel, so lets set the EE and dir */
> > + bchan->ee = args->ee;
> > + bchan->slave.direction = args->dir ?
> > + DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
> > + return true;
> > + }
> > +
> > + return false;
> > +}
> > +
> > +static struct dma_chan *bam_dma_xlate(struct of_phandle_args *dma_spec,
> > + struct of_dma *of)
> > +{
> > + struct bam_filter_args args;
> > + dma_cap_mask_t cap;
> > +
> > + if (dma_spec->args_count != 3)
> > + return NULL;
> > +
> > + args.dev = of->of_dma_data;
> > + args.id = dma_spec->args[0];
> > + args.ee = dma_spec->args[1];
> > + args.dir = dma_spec->args[2];
> > +
> > + dma_cap_zero(cap);
> > + dma_cap_set(DMA_SLAVE, cap);
> > +
> > + return dma_request_channel(cap, bam_dma_filter, &args);
> > +}
> > +
> > +/**
> > + * bam_init
> > + * @bdev: bam device
> > + *
> > + * Initialization helper for global bam registers
> > + */
> > +static void bam_init(struct bam_device *bdev)
> > +{
> > + u32 val;
> > +
> > + /* read versioning information */
> > + val = readl_relaxed(bdev->regs + BAM_REVISION);
> > + bdev->num_ees = val & NUM_EES_MASK;
> > +
> > + val = readl_relaxed(bdev->regs + BAM_NUM_PIPES);
> > + bdev->num_channels = val & BAM_NUM_PIPES_MASK;
> > +
> > + /* s/w reset bam */
> > + /* after reset all pipes are disabled and idle */
> > + val = readl_relaxed(bdev->regs + BAM_CTRL);
> > + val |= BAM_SW_RST;
> > + writel_relaxed(val, bdev->regs + BAM_CTRL);
> > + val &= ~BAM_SW_RST;
> > + writel_relaxed(val, bdev->regs + BAM_CTRL);
> > +
> > + /* make sure previous stores are visible before enabling BAM */
> > + wmb();
> > +
> > + /* enable bam */
> > + val |= BAM_EN;
> > + writel_relaxed(val, bdev->regs + BAM_CTRL);
> > +
> > + /* set descriptor threshhold, start with 4 bytes */
> > + writel_relaxed(DEFAULT_CNT_THRSHLD, bdev->regs + BAM_DESC_CNT_TRSHLD);
> > +
> > + /* Enable default set of h/w workarounds, ie all except BAM_FULL_PIPE */
> > + writel_relaxed(BAM_CNFG_BITS_DEFAULT, bdev->regs + BAM_CNFG_BITS);
> > +
> > + /* enable irqs for errors */
> > + writel_relaxed(BAM_ERROR_EN | BAM_HRESP_ERR_EN,
> > + bdev->regs + BAM_IRQ_EN);
> > +}
> > +
> > +static void bam_channel_init(struct bam_device *bdev, struct bam_chan *bchan,
> > + u32 index)
> > +{
> > + bchan->id = index;
> > + bchan->bdev = bdev;
> > +
> > + vchan_init(&bchan->vc, &bdev->common);
> > + bchan->vc.desc_free = bam_dma_free_desc;
> > +
> > + bam_reset_channel(bdev, bchan->id);
> > +}
> > +
> > +static int bam_dma_probe(struct platform_device *pdev)
> > +{
> > + struct bam_device *bdev;
> > + struct resource *iores, *irq_res;
> > + int ret, i;
> > +
> > + bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL);
> > + if (!bdev)
> > + return -ENOMEM;
> > +
> > + bdev->dev = &pdev->dev;
> > +
> > + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + if (!iores) {
> > + dev_err(bdev->dev, "register resource is missing\n");
> > + return -EINVAL;
> > + }
>
> Useless check and messaging, devm_ioremap_resource will do this for you.
>
Will fix this along with the other resource comment.
> > +
> > + bdev->regs = devm_ioremap_resource(&pdev->dev, iores);
> > + if (IS_ERR(bdev->regs))
> > + return PTR_ERR(bdev->regs);
> > +
> > + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> > + if (!irq_res) {
> > + dev_err(bdev->dev, "irq resource is missing\n");
> > + return -EINVAL;
> > + }
> > +
> > + bdev->bamclk = devm_clk_get(bdev->dev, "bam_clk");
> > + if (IS_ERR(bdev->bamclk))
> > + return PTR_ERR(bdev->bamclk);
> > +
> > + ret = clk_prepare_enable(bdev->bamclk);
> > + if (ret) {
> > + dev_err(bdev->dev, "failed to prepare/enable clock");
> > + return ret;
> > + }
> > +
> > + bam_init(bdev);
> > +
> > + tasklet_init(&bdev->task, dma_tasklet, (unsigned long)bdev);
> > +
> > + bdev->channels = devm_kzalloc(bdev->dev,
>
> devm_kcalloc.
>
Will fix.
> > + sizeof(*bdev->channels) * bdev->num_channels,
> > + GFP_KERNEL);
> > +
> > + if (!bdev->channels) {
> > + ret = -ENOMEM;
> > + goto err_disable_clk;
> > + }
> > +
> > + /* allocate and initialize channels */
> > + INIT_LIST_HEAD(&bdev->common.channels);
> > +
> > + for (i = 0; i < bdev->num_channels; i++)
> > + bam_channel_init(bdev, &bdev->channels[i], i);
> > +
> > + ret = devm_request_irq(bdev->dev, irq_res->start, bam_dma_irq,
> > + IRQF_TRIGGER_HIGH, "bam_dma", bdev);
> > + if (ret) {
> > + dev_err(bdev->dev, "cannot register IRQ\n");
> > + goto err_disable_clk;
> > + }
> > +
> > + /* set max dma segment size */
> > + bdev->common.dev = bdev->dev;
> > + bdev->common.dev->dma_parms = &bdev->dma_parms;
> > + ret = dma_set_max_seg_size(bdev->common.dev, BAM_MAX_DATA_SIZE);
> > + if (ret) {
> > + dev_err(bdev->dev, "cannot set maximum segment size\n");
> > + goto err_disable_clk;
> > + }
> > +
> > + platform_set_drvdata(pdev, bdev);
> > +
> > + /* set capabilities */
> > + dma_cap_zero(bdev->common.cap_mask);
> > + dma_cap_set(DMA_SLAVE, bdev->common.cap_mask);
> > +
> > + /* initialize dmaengine apis */
> > + bdev->common.device_alloc_chan_resources = bam_alloc_chan;
> > + bdev->common.device_free_chan_resources = bam_free_chan;
> > + bdev->common.device_prep_slave_sg = bam_prep_slave_sg;
> > + bdev->common.device_control = bam_control;
> > + bdev->common.device_issue_pending = bam_issue_pending;
> > + bdev->common.device_tx_status = bam_tx_status;
> > + bdev->common.dev = bdev->dev;
> > +
> > + ret = dma_async_device_register(&bdev->common);
> > + if (ret) {
> > + dev_err(bdev->dev, "failed to register dma async device\n");
> > + goto err_disable_clk;
> > + }
> > +
> > + if (pdev->dev.of_node) {
> > + ret = of_dma_controller_register(pdev->dev.of_node,
> > + bam_dma_xlate, &bdev->common);
> > +
> > + if (ret) {
> > + dev_err(bdev->dev, "failed to register of_dma\n");
> > + goto err_unregister_dma;
> > + }
> > + }
> > +
> > + return 0;
> > +
> > +err_unregister_dma:
> > + dma_async_device_unregister(&bdev->common);
> > +err_disable_clk:
> > + clk_disable_unprepare(bdev->bamclk);
>
>
>
> > + return ret;
> > +}
> > +
> > +static int bam_dma_remove(struct platform_device *pdev)
> > +{
> > + struct bam_device *bdev = platform_get_drvdata(pdev);
> > +
> > + dma_async_device_unregister(&bdev->common);
> > +
> > + if (pdev->dev.of_node)
> > + of_dma_controller_free(pdev->dev.of_node);
> > +
> > + clk_disable_unprepare(bdev->bamclk);
> > +
> > + return 0;
> > +}
> > +
> > +#ifdef CONFIG_OF
> > +static const struct of_device_id bam_of_match[] = {
> > + { .compatible = "qcom,bam-v1.4.0", },
> > + { .compatible = "qcom,bam-v1.4.1", },
> > + {}
> > +};
> > +MODULE_DEVICE_TABLE(of, bam_of_match);
> > +#endif
> > +
> > +static struct platform_driver bam_dma_driver = {
> > + .probe = bam_dma_probe,
> > + .remove = bam_dma_remove,
> > + .driver = {
> > + .name = "bam-dma-engine",
> > + .owner = THIS_MODULE,
> > + .of_match_table = of_match_ptr(bam_of_match),
> > + },
> > +};
> > +
> > +static int __init bam_dma_init(void)
> > +{
> > + return platform_driver_register(&bam_dma_driver);
> > +}
> > +
> > +static void __exit bam_dma_exit(void)
> > +{
> > + return platform_driver_unregister(&bam_dma_driver);
> > +}
> > +
> > +module_init(bam_dma_init);
> > +module_exit(bam_dma_exit);
>
> module_platform_driver() ?
>
Will fix.
> > +
> > +MODULE_AUTHOR("Andy Gross <agross at codeaurora.org>");
> > +MODULE_DESCRIPTION("QCOM BAM DMA engine driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/dma/qcom_bam_dma.h b/drivers/dma/qcom_bam_dma.h
> > new file mode 100644
> > index 0000000..2cb3b5f
> > --- /dev/null
> > +++ b/drivers/dma/qcom_bam_dma.h
> > @@ -0,0 +1,268 @@
> > +/*
> > + * Copyright (c) 2013, The Linux Foundation. All rights reserved.
>
> 2014 ?
>
Should probably be 2013-2014 since development has spanned the change in year.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 and
> > + * only 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 __QCOM_BAM_DMA_H__
> > +#define __QCOM_BAM_DMA_H__
> > +
> > +#include <linux/dmaengine.h>
> > +#include "virt-dma.h"
> > +
> > +enum bam_channel_dir {
> > + BAM_PIPE_CONSUMER = 0, /* channel reads from data-fifo or memory */
> > + BAM_PIPE_PRODUCER, /* channel writes to data-fifo or memory */
> > +};
> > +
> > +struct bam_desc_hw {
> > + u32 addr; /* Buffer physical address */
> > + u16 size; /* Buffer size in bytes */
> > + u16 flags;
> > +} __packed;
> > +
> > +#define DESC_FLAG_INT BIT(15)
> > +#define DESC_FLAG_EOT BIT(14)
> > +#define DESC_FLAG_EOB BIT(13)
> > +
> > +struct bam_async_desc {
> > + struct virt_dma_desc vd;
> > +
> > + u32 num_desc;
> > + u32 xfer_len;
> > + struct bam_desc_hw *curr_desc;
> > +
> > + enum bam_channel_dir dir;
> > + size_t length;
> > + struct bam_desc_hw desc[0];
> > +};
> > +
> > +#define BAM_CTRL 0x0000
> > +#define BAM_REVISION 0x0004
> > +#define BAM_SW_REVISION 0x0080
> > +#define BAM_NUM_PIPES 0x003C
> > +#define BAM_TIMER 0x0040
> > +#define BAM_TIMER_CTRL 0x0044
> > +#define BAM_DESC_CNT_TRSHLD 0x0008
> > +#define BAM_IRQ_SRCS 0x000C
> > +#define BAM_IRQ_SRCS_MSK 0x0010
> > +#define BAM_IRQ_SRCS_UNMASKED 0x0030
> > +#define BAM_IRQ_STTS 0x0014
> > +#define BAM_IRQ_CLR 0x0018
> > +#define BAM_IRQ_EN 0x001C
> > +#define BAM_CNFG_BITS 0x007C
> > +#define BAM_IRQ_SRCS_EE(pipe) (0x0800 + ((pipe) * 0x80))
> > +#define BAM_IRQ_SRCS_MSK_EE(pipe) (0x0804 + ((pipe) * 0x80))
> > +#define BAM_P_CTRL(pipe) (0x1000 + ((pipe) * 0x1000))
> > +#define BAM_P_RST(pipe) (0x1004 + ((pipe) * 0x1000))
> > +#define BAM_P_HALT(pipe) (0x1008 + ((pipe) * 0x1000))
> > +#define BAM_P_IRQ_STTS(pipe) (0x1010 + ((pipe) * 0x1000))
> > +#define BAM_P_IRQ_CLR(pipe) (0x1014 + ((pipe) * 0x1000))
> > +#define BAM_P_IRQ_EN(pipe) (0x1018 + ((pipe) * 0x1000))
> > +#define BAM_P_EVNT_DEST_ADDR(pipe) (0x182C + ((pipe) * 0x1000))
> > +#define BAM_P_EVNT_REG(pipe) (0x1818 + ((pipe) * 0x1000))
> > +#define BAM_P_SW_OFSTS(pipe) (0x1800 + ((pipe) * 0x1000))
> > +#define BAM_P_DATA_FIFO_ADDR(pipe) (0x1824 + ((pipe) * 0x1000))
> > +#define BAM_P_DESC_FIFO_ADDR(pipe) (0x181C + ((pipe) * 0x1000))
> > +#define BAM_P_EVNT_TRSHLD(pipe) (0x1828 + ((pipe) * 0x1000))
> > +#define BAM_P_FIFO_SIZES(pipe) (0x1820 + ((pipe) * 0x1000))
> > +
> > +/* BAM CTRL */
> > +#define BAM_SW_RST BIT(0)
> > +#define BAM_EN BIT(1)
> > +#define BAM_EN_ACCUM BIT(4)
> > +#define BAM_TESTBUS_SEL_SHIFT 5
> > +#define BAM_TESTBUS_SEL_MASK 0x3F
> > +#define BAM_DESC_CACHE_SEL_SHIFT 13
> > +#define BAM_DESC_CACHE_SEL_MASK 0x3
> > +#define BAM_CACHED_DESC_STORE BIT(15)
> > +#define IBC_DISABLE BIT(16)
> > +
> > +/* BAM REVISION */
> > +#define REVISION_SHIFT 0
> > +#define REVISION_MASK 0xFF
> > +#define NUM_EES_SHIFT 8
> > +#define NUM_EES_MASK 0xF
> > +#define CE_BUFFER_SIZE BIT(13)
> > +#define AXI_ACTIVE BIT(14)
> > +#define USE_VMIDMT BIT(15)
> > +#define SECURED BIT(16)
> > +#define BAM_HAS_NO_BYPASS BIT(17)
> > +#define HIGH_FREQUENCY_BAM BIT(18)
> > +#define INACTIV_TMRS_EXST BIT(19)
> > +#define NUM_INACTIV_TMRS BIT(20)
> > +#define DESC_CACHE_DEPTH_SHIFT 21
> > +#define DESC_CACHE_DEPTH_1 (0 << DESC_CACHE_DEPTH_SHIFT)
> > +#define DESC_CACHE_DEPTH_2 (1 << DESC_CACHE_DEPTH_SHIFT)
> > +#define DESC_CACHE_DEPTH_3 (2 << DESC_CACHE_DEPTH_SHIFT)
> > +#define DESC_CACHE_DEPTH_4 (3 << DESC_CACHE_DEPTH_SHIFT)
> > +#define CMD_DESC_EN BIT(23)
> > +#define INACTIV_TMR_BASE_SHIFT 24
> > +#define INACTIV_TMR_BASE_MASK 0xFF
> > +
> > +/* BAM NUM PIPES */
> > +#define BAM_NUM_PIPES_SHIFT 0
> > +#define BAM_NUM_PIPES_MASK 0xFF
> > +#define PERIPH_NON_PIPE_GRP_SHIFT 16
> > +#define PERIPH_NON_PIP_GRP_MASK 0xFF
> > +#define BAM_NON_PIPE_GRP_SHIFT 24
> > +#define BAM_NON_PIPE_GRP_MASK 0xFF
> > +
> > +/* BAM CNFG BITS */
> > +#define BAM_PIPE_CNFG BIT(2)
> > +#define BAM_FULL_PIPE BIT(11)
> > +#define BAM_NO_EXT_P_RST BIT(12)
> > +#define BAM_IBC_DISABLE BIT(13)
> > +#define BAM_SB_CLK_REQ BIT(14)
> > +#define BAM_PSM_CSW_REQ BIT(15)
> > +#define BAM_PSM_P_RES BIT(16)
> > +#define BAM_AU_P_RES BIT(17)
> > +#define BAM_SI_P_RES BIT(18)
> > +#define BAM_WB_P_RES BIT(19)
> > +#define BAM_WB_BLK_CSW BIT(20)
> > +#define BAM_WB_CSW_ACK_IDL BIT(21)
> > +#define BAM_WB_RETR_SVPNT BIT(22)
> > +#define BAM_WB_DSC_AVL_P_RST BIT(23)
> > +#define BAM_REG_P_EN BIT(24)
> > +#define BAM_PSM_P_HD_DATA BIT(25)
> > +#define BAM_AU_ACCUMED BIT(26)
> > +#define BAM_CMD_ENABLE BIT(27)
> > +
> > +#define BAM_CNFG_BITS_DEFAULT (BAM_PIPE_CNFG | \
> > + BAM_NO_EXT_P_RST | \
> > + BAM_IBC_DISABLE | \
> > + BAM_SB_CLK_REQ | \
> > + BAM_PSM_CSW_REQ | \
> > + BAM_PSM_P_RES | \
> > + BAM_AU_P_RES | \
> > + BAM_SI_P_RES | \
> > + BAM_WB_P_RES | \
> > + BAM_WB_BLK_CSW | \
> > + BAM_WB_CSW_ACK_IDL | \
> > + BAM_WB_RETR_SVPNT | \
> > + BAM_WB_DSC_AVL_P_RST | \
> > + BAM_REG_P_EN | \
> > + BAM_PSM_P_HD_DATA | \
> > + BAM_AU_ACCUMED | \
> > + BAM_CMD_ENABLE)
> > +
> > +/* PIPE CTRL */
> > +#define P_EN BIT(1)
> > +#define P_DIRECTION BIT(3)
> > +#define P_SYS_STRM BIT(4)
> > +#define P_SYS_MODE BIT(5)
> > +#define P_AUTO_EOB BIT(6)
> > +#define P_AUTO_EOB_SEL_SHIFT 7
> > +#define P_AUTO_EOB_SEL_512 (0 << P_AUTO_EOB_SEL_SHIFT)
> > +#define P_AUTO_EOB_SEL_256 (1 << P_AUTO_EOB_SEL_SHIFT)
> > +#define P_AUTO_EOB_SEL_128 (2 << P_AUTO_EOB_SEL_SHIFT)
> > +#define P_AUTO_EOB_SEL_64 (3 << P_AUTO_EOB_SEL_SHIFT)
> > +#define P_PREFETCH_LIMIT_SHIFT 9
> > +#define P_PREFETCH_LIMIT_32 (0 << P_PREFETCH_LIMIT_SHIFT)
> > +#define P_PREFETCH_LIMIT_16 (1 << P_PREFETCH_LIMIT_SHIFT)
> > +#define P_PREFETCH_LIMIT_4 (2 << P_PREFETCH_LIMIT_SHIFT)
> > +#define P_WRITE_NWD BIT(11)
> > +#define P_LOCK_GROUP_SHIFT 16
> > +#define P_LOCK_GROUP_MASK 0x1F
> > +
> > +/* BAM_DESC_CNT_TRSHLD */
> > +#define CNT_TRSHLD 0xffff
> > +#define DEFAULT_CNT_THRSHLD 0x4
> > +
> > +/* BAM_IRQ_SRCS */
> > +#define BAM_IRQ BIT(31)
> > +#define P_IRQ 0x7fffffff
> > +
> > +/* BAM_IRQ_SRCS_MSK */
> > +#define BAM_IRQ_MSK BAM_IRQ
> > +#define P_IRQ_MSK P_IRQ
> > +
> > +/* BAM_IRQ_STTS */
> > +#define BAM_TIMER_IRQ BIT(4)
> > +#define BAM_EMPTY_IRQ BIT(3)
> > +#define BAM_ERROR_IRQ BIT(2)
> > +#define BAM_HRESP_ERR_IRQ BIT(1)
> > +
> > +/* BAM_IRQ_CLR */
> > +#define BAM_TIMER_CLR BIT(4)
> > +#define BAM_EMPTY_CLR BIT(3)
> > +#define BAM_ERROR_CLR BIT(2)
> > +#define BAM_HRESP_ERR_CLR BIT(1)
> > +
> > +/* BAM_IRQ_EN */
> > +#define BAM_TIMER_EN BIT(4)
> > +#define BAM_EMPTY_EN BIT(3)
> > +#define BAM_ERROR_EN BIT(2)
> > +#define BAM_HRESP_ERR_EN BIT(1)
> > +
> > +/* BAM_P_IRQ_EN */
> > +#define P_PRCSD_DESC_EN BIT(0)
> > +#define P_TIMER_EN BIT(1)
> > +#define P_WAKE_EN BIT(2)
> > +#define P_OUT_OF_DESC_EN BIT(3)
> > +#define P_ERR_EN BIT(4)
> > +#define P_TRNSFR_END_EN BIT(5)
> > +#define P_DEFAULT_IRQS_EN (P_PRCSD_DESC_EN | P_ERR_EN | P_TRNSFR_END_EN)
> > +
> > +/* BAM_P_SW_OFSTS */
> > +#define P_SW_OFSTS_MASK 0xffff
> > +
> > +#define BAM_DESC_FIFO_SIZE SZ_32K
> > +#define MAX_DESCRIPTORS (BAM_DESC_FIFO_SIZE / sizeof(struct bam_desc_hw) - 1)
> > +#define BAM_MAX_DATA_SIZE (SZ_32K - 8)
> > +
> > +struct bam_chan {
> > + struct virt_dma_chan vc;
> > +
> > + struct bam_device *bdev;
> > +
> > + /* configuration from device tree */
> > + u32 id;
> > + u32 ee;
> > +
> > + struct bam_async_desc *curr_txd; /* current running dma */
> > +
> > + /* runtime configuration */
> > + struct dma_slave_config slave;
> > +
> > + /* fifo storage */
> > + struct bam_desc_hw *fifo_virt;
> > + dma_addr_t fifo_phys;
> > +
> > + /* fifo markers */
> > + unsigned short head; /* start of active descriptor entries */
> > + unsigned short tail; /* end of active descriptor entries */
> > +
> > + unsigned int paused; /* is the channel paused? */
> > +
> > + struct list_head node;
> > +};
> > +
> > +static inline struct bam_chan *to_bam_chan(struct dma_chan *common)
> > +{
> > + return container_of(common, struct bam_chan, vc.chan);
> > +}
> > +
> > +struct bam_device {
> > + void __iomem *regs;
> > + struct device *dev;
> > + struct dma_device common;
> > + struct device_dma_parameters dma_parms;
> > + struct bam_chan *channels;
> > + u32 num_channels;
> > + u32 num_ees;
> > + unsigned long enabled_ees;
> > + int irq;
> > + struct clk *bamclk;
> > +
> > + /* dma start transaction tasklet */
> > + struct tasklet_struct task;
> > +};
> > +
> > +#endif /* __QCOM_BAM_DMA_H__ */
>
> --
> Andy Shevchenko <andriy.shevchenko at intel.com>
> Intel Finland Oy
> ---------------------------------------------------------------------
> Intel Finland Oy
> Registered Address: PL 281, 00181 Helsinki
> Business Identity Code: 0357606 - 4
> Domiciled in Helsinki
>
> This e-mail and any attachments may contain confidential material for
> the sole use of the intended recipient(s). Any review or distribution
> by others is strictly prohibited. If you are not the intended
> recipient, please contact the sender and delete all copies.
--
sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
More information about the linux-arm-kernel
mailing list