Subject: [PATCH v2 1/2] dmaengine: add Qualcomm BAM dma driver

From: [email-masked] (Andy Gross)
Date: Mon, 20 Jan 2014 17:31:54 -0600

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 <[email-masked]>
> > ---
> >  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 <[email-masked]>");
> > +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 <[email-masked]>
> 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