diff --git a/arch/arm/mach-bcm2708/Makefile b/arch/arm/mach-bcm2708/Makefile index 21e352166d9ea..454408cbeade8 100644 --- a/arch/arm/mach-bcm2708/Makefile +++ b/arch/arm/mach-bcm2708/Makefile @@ -2,6 +2,6 @@ # Makefile for the linux kernel. # -obj-$(CONFIG_MACH_BCM2708) += bcm2708.o armctrl.o vcio.o power.o dma.o +obj-$(CONFIG_MACH_BCM2708) += bcm2708.o armctrl.o vcio.o power.o obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o diff --git a/arch/arm/mach-bcm2708/include/mach/dma.h b/arch/arm/mach-bcm2708/include/mach/dma.h index d03e7b5a1f6b0..d826705e1574e 100644 --- a/arch/arm/mach-bcm2708/include/mach/dma.h +++ b/arch/arm/mach-bcm2708/include/mach/dma.h @@ -1,94 +1,2 @@ -/* - * linux/arch/arm/mach-bcm2708/include/mach/dma.h - * - * Copyright (C) 2010 Broadcom - * - * 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. - */ - - -#ifndef _MACH_BCM2708_DMA_H -#define _MACH_BCM2708_DMA_H - -#define BCM_DMAMAN_DRIVER_NAME "bcm2708_dma" - -/* DMA CS Control and Status bits */ -#define BCM2708_DMA_ACTIVE (1 << 0) -#define BCM2708_DMA_INT (1 << 2) -#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */ -#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */ -#define BCM2708_DMA_ERR (1 << 8) -#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */ -#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */ - -/* DMA control block "info" field bits */ -#define BCM2708_DMA_INT_EN (1 << 0) -#define BCM2708_DMA_TDMODE (1 << 1) -#define BCM2708_DMA_WAIT_RESP (1 << 3) -#define BCM2708_DMA_D_INC (1 << 4) -#define BCM2708_DMA_D_WIDTH (1 << 5) -#define BCM2708_DMA_D_DREQ (1 << 6) -#define BCM2708_DMA_S_INC (1 << 8) -#define BCM2708_DMA_S_WIDTH (1 << 9) -#define BCM2708_DMA_S_DREQ (1 << 10) - -#define BCM2708_DMA_BURST(x) (((x)&0xf) << 12) -#define BCM2708_DMA_PER_MAP(x) ((x) << 16) -#define BCM2708_DMA_WAITS(x) (((x)&0x1f) << 21) - -#define BCM2708_DMA_DREQ_EMMC 11 -#define BCM2708_DMA_DREQ_SDHOST 13 - -#define BCM2708_DMA_CS 0x00 /* Control and Status */ -#define BCM2708_DMA_ADDR 0x04 -/* the current control block appears in the following registers - read only */ -#define BCM2708_DMA_INFO 0x08 -#define BCM2708_DMA_SOURCE_AD 0x0c -#define BCM2708_DMA_DEST_AD 0x10 -#define BCM2708_DMA_NEXTCB 0x1C -#define BCM2708_DMA_DEBUG 0x20 - -#define BCM2708_DMA4_CS (BCM2708_DMA_CHAN(4)+BCM2708_DMA_CS) -#define BCM2708_DMA4_ADDR (BCM2708_DMA_CHAN(4)+BCM2708_DMA_ADDR) - -#define BCM2708_DMA_TDMODE_LEN(w, h) ((h) << 16 | (w)) - -struct bcm2708_dma_cb { - unsigned long info; - unsigned long src; - unsigned long dst; - unsigned long length; - unsigned long stride; - unsigned long next; - unsigned long pad[2]; -}; -struct scatterlist; - -extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len); -extern void bcm_dma_start(void __iomem *dma_chan_base, - dma_addr_t control_block); -extern void bcm_dma_wait_idle(void __iomem *dma_chan_base); -extern bool bcm_dma_is_busy(void __iomem *dma_chan_base); -extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base); - -/* When listing features we can ask for when allocating DMA channels give - those with higher priority smaller ordinal numbers */ -#define BCM_DMA_FEATURE_FAST_ORD 0 -#define BCM_DMA_FEATURE_BULK_ORD 1 -#define BCM_DMA_FEATURE_NORMAL_ORD 2 -#define BCM_DMA_FEATURE_LITE_ORD 3 -#define BCM_DMA_FEATURE_FAST (1< diff --git a/arch/arm/mach-bcm2709/Makefile b/arch/arm/mach-bcm2709/Makefile index 2a803bb6bf45d..f07c38b77aba5 100644 --- a/arch/arm/mach-bcm2709/Makefile +++ b/arch/arm/mach-bcm2709/Makefile @@ -2,6 +2,6 @@ # Makefile for the linux kernel. # -obj-$(CONFIG_MACH_BCM2709) += bcm2709.o armctrl.o vcio.o power.o dma.o +obj-$(CONFIG_MACH_BCM2709) += bcm2709.o armctrl.o vcio.o power.o obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o diff --git a/arch/arm/mach-bcm2709/include/mach/dma.h b/arch/arm/mach-bcm2709/include/mach/dma.h index d03e7b5a1f6b0..d826705e1574e 100644 --- a/arch/arm/mach-bcm2709/include/mach/dma.h +++ b/arch/arm/mach-bcm2709/include/mach/dma.h @@ -1,94 +1,2 @@ -/* - * linux/arch/arm/mach-bcm2708/include/mach/dma.h - * - * Copyright (C) 2010 Broadcom - * - * 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. - */ - - -#ifndef _MACH_BCM2708_DMA_H -#define _MACH_BCM2708_DMA_H - -#define BCM_DMAMAN_DRIVER_NAME "bcm2708_dma" - -/* DMA CS Control and Status bits */ -#define BCM2708_DMA_ACTIVE (1 << 0) -#define BCM2708_DMA_INT (1 << 2) -#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */ -#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */ -#define BCM2708_DMA_ERR (1 << 8) -#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */ -#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */ - -/* DMA control block "info" field bits */ -#define BCM2708_DMA_INT_EN (1 << 0) -#define BCM2708_DMA_TDMODE (1 << 1) -#define BCM2708_DMA_WAIT_RESP (1 << 3) -#define BCM2708_DMA_D_INC (1 << 4) -#define BCM2708_DMA_D_WIDTH (1 << 5) -#define BCM2708_DMA_D_DREQ (1 << 6) -#define BCM2708_DMA_S_INC (1 << 8) -#define BCM2708_DMA_S_WIDTH (1 << 9) -#define BCM2708_DMA_S_DREQ (1 << 10) - -#define BCM2708_DMA_BURST(x) (((x)&0xf) << 12) -#define BCM2708_DMA_PER_MAP(x) ((x) << 16) -#define BCM2708_DMA_WAITS(x) (((x)&0x1f) << 21) - -#define BCM2708_DMA_DREQ_EMMC 11 -#define BCM2708_DMA_DREQ_SDHOST 13 - -#define BCM2708_DMA_CS 0x00 /* Control and Status */ -#define BCM2708_DMA_ADDR 0x04 -/* the current control block appears in the following registers - read only */ -#define BCM2708_DMA_INFO 0x08 -#define BCM2708_DMA_SOURCE_AD 0x0c -#define BCM2708_DMA_DEST_AD 0x10 -#define BCM2708_DMA_NEXTCB 0x1C -#define BCM2708_DMA_DEBUG 0x20 - -#define BCM2708_DMA4_CS (BCM2708_DMA_CHAN(4)+BCM2708_DMA_CS) -#define BCM2708_DMA4_ADDR (BCM2708_DMA_CHAN(4)+BCM2708_DMA_ADDR) - -#define BCM2708_DMA_TDMODE_LEN(w, h) ((h) << 16 | (w)) - -struct bcm2708_dma_cb { - unsigned long info; - unsigned long src; - unsigned long dst; - unsigned long length; - unsigned long stride; - unsigned long next; - unsigned long pad[2]; -}; -struct scatterlist; - -extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len); -extern void bcm_dma_start(void __iomem *dma_chan_base, - dma_addr_t control_block); -extern void bcm_dma_wait_idle(void __iomem *dma_chan_base); -extern bool bcm_dma_is_busy(void __iomem *dma_chan_base); -extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base); - -/* When listing features we can ask for when allocating DMA channels give - those with higher priority smaller ordinal numbers */ -#define BCM_DMA_FEATURE_FAST_ORD 0 -#define BCM_DMA_FEATURE_BULK_ORD 1 -#define BCM_DMA_FEATURE_NORMAL_ORD 2 -#define BCM_DMA_FEATURE_LITE_ORD 3 -#define BCM_DMA_FEATURE_FAST (1< diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 2395f637f7d46..41097c744a6a9 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -339,10 +339,15 @@ config DMA_BCM2835 config DMA_BCM2708 tristate "BCM2708 DMA engine support" - depends on MACH_BCM2708 || MACH_BCM2709 + depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835 select DMA_ENGINE select DMA_VIRTUAL_CHANNELS +config DMA_BCM2708_LEGACY + bool "BCM2708 DMA legacy API support" + depends on DMA_BCM2708 + default y + config TI_CPPI41 tristate "AM33xx CPPI41 DMA support" depends on ARCH_OMAP @@ -381,7 +386,7 @@ config MOXART_DMA select DMA_VIRTUAL_CHANNELS help Enable support for the MOXA ART SoC DMA controller. - + config FSL_EDMA tristate "Freescale eDMA engine support" depends on OF diff --git a/drivers/dma/bcm2708-dmaengine.c b/drivers/dma/bcm2708-dmaengine.c index 8182b1622cc09..937fd60c50922 100644 --- a/drivers/dma/bcm2708-dmaengine.c +++ b/drivers/dma/bcm2708-dmaengine.c @@ -37,26 +37,304 @@ #include #include #include +#include #include #include #include #include +#include +#include -#ifndef CONFIG_ARCH_BCM2835 +#include "virt-dma.h" -/* dma manager */ -#include +static unsigned dma_debug; -//#define DMA_COMPLETE DMA_SUCCESS +/* + * Legacy DMA API + */ -#endif +#ifdef CONFIG_DMA_BCM2708_LEGACY -#include -#include +#define CACHE_LINE_MASK 31 +#define DEFAULT_DMACHAN_BITMAP 0x10 /* channel 4 only */ -#include "virt-dma.h" +/* valid only for channels 0 - 14, 15 has its own base address */ +#define BCM2708_DMA_CHAN(n) ((n) << 8) /* base address */ +#define BCM2708_DMA_CHANIO(dma_base, n) \ + ((void __iomem *)((char *)(dma_base) + BCM2708_DMA_CHAN(n))) -static unsigned dma_debug; +struct vc_dmaman { + void __iomem *dma_base; + u32 chan_available; /* bitmap of available channels */ + u32 has_feature[BCM_DMA_FEATURE_COUNT]; /* bitmap of feature presence */ + struct mutex lock; +}; + +static struct device *dmaman_dev; /* we assume there's only one! */ +static struct vc_dmaman *g_dmaman; /* DMA manager */ +static int dmachans = -1; /* module parameter */ + +/* DMA Auxiliary Functions */ + +/* A DMA buffer on an arbitrary boundary may separate a cache line into a + section inside the DMA buffer and another section outside it. + Even if we flush DMA buffers from the cache there is always the chance that + during a DMA someone will access the part of a cache line that is outside + the DMA buffer - which will then bring in unwelcome data. + Without being able to dictate our own buffer pools we must insist that + DMA buffers consist of a whole number of cache lines. +*/ +extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len) +{ + int i; + + for (i = 0; i < sg_len; i++) { + if (sg_ptr[i].offset & CACHE_LINE_MASK || + sg_ptr[i].length & CACHE_LINE_MASK) + return 0; + } + + return 1; +} +EXPORT_SYMBOL_GPL(bcm_sg_suitable_for_dma); + +extern void bcm_dma_start(void __iomem *dma_chan_base, + dma_addr_t control_block) +{ + dsb(); /* ARM data synchronization (push) operation */ + + writel(control_block, dma_chan_base + BCM2708_DMA_ADDR); + writel(BCM2708_DMA_ACTIVE, dma_chan_base + BCM2708_DMA_CS); +} +EXPORT_SYMBOL_GPL(bcm_dma_start); + +extern void bcm_dma_wait_idle(void __iomem *dma_chan_base) +{ + dsb(); + + /* ugly busy wait only option for now */ + while (readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE) + cpu_relax(); +} +EXPORT_SYMBOL_GPL(bcm_dma_wait_idle); + +extern bool bcm_dma_is_busy(void __iomem *dma_chan_base) +{ + dsb(); + + return readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE; +} +EXPORT_SYMBOL_GPL(bcm_dma_is_busy); + +/* Complete an ongoing DMA (assuming its results are to be ignored) + Does nothing if there is no DMA in progress. + This routine waits for the current AXI transfer to complete before + terminating the current DMA. If the current transfer is hung on a DREQ used + by an uncooperative peripheral the AXI transfer may never complete. In this + case the routine times out and return a non-zero error code. + Use of this routine doesn't guarantee that the ongoing or aborted DMA + does not produce an interrupt. +*/ +extern int bcm_dma_abort(void __iomem *dma_chan_base) +{ + unsigned long int cs; + int rc = 0; + + cs = readl(dma_chan_base + BCM2708_DMA_CS); + + if (BCM2708_DMA_ACTIVE & cs) { + long int timeout = 10000; + + /* write 0 to the active bit - pause the DMA */ + writel(0, dma_chan_base + BCM2708_DMA_CS); + + /* wait for any current AXI transfer to complete */ + while (0 != (cs & BCM2708_DMA_ISPAUSED) && --timeout >= 0) + cs = readl(dma_chan_base + BCM2708_DMA_CS); + + if (0 != (cs & BCM2708_DMA_ISPAUSED)) { + /* we'll un-pause when we set of our next DMA */ + rc = -ETIMEDOUT; + + } else if (BCM2708_DMA_ACTIVE & cs) { + /* terminate the control block chain */ + writel(0, dma_chan_base + BCM2708_DMA_NEXTCB); + + /* abort the whole DMA */ + writel(BCM2708_DMA_ABORT | BCM2708_DMA_ACTIVE, + dma_chan_base + BCM2708_DMA_CS); + } + } + + return rc; +} +EXPORT_SYMBOL_GPL(bcm_dma_abort); + + /* DMA Manager Device Methods */ + +static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base, + u32 chans_available) +{ + dmaman->dma_base = dma_base; + dmaman->chan_available = chans_available; + dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c; /* 2 & 3 */ + dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* 0 */ + dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* 1 to 7 */ + dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* 8 to 14 */ +} + +static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, + unsigned preferred_feature_set) +{ + u32 chans; + int chan = 0; + int feature; + + chans = dmaman->chan_available; + for (feature = 0; feature < BCM_DMA_FEATURE_COUNT; feature++) + /* select the subset of available channels with the desired + feature so long as some of the candidate channels have that + feature */ + if ((preferred_feature_set & (1 << feature)) && + (chans & dmaman->has_feature[feature])) + chans &= dmaman->has_feature[feature]; + + if (!chans) + return -ENOENT; + + /* return the ordinal of the first channel in the bitmap */ + while (chans != 0 && (chans & 1) == 0) { + chans >>= 1; + chan++; + } + /* claim the channel */ + dmaman->chan_available &= ~(1 << chan); + + return chan; +} + +static int vc_dmaman_chan_free(struct vc_dmaman *dmaman, int chan) +{ + if (chan < 0) + return -EINVAL; + + if ((1 << chan) & dmaman->chan_available) + return -EIDRM; + + dmaman->chan_available |= (1 << chan); + + return 0; +} + +/* DMA Manager Monitor */ + +extern int bcm_dma_chan_alloc(unsigned preferred_feature_set, + void __iomem **out_dma_base, int *out_dma_irq) +{ + struct vc_dmaman *dmaman = g_dmaman; + struct platform_device *pdev = to_platform_device(dmaman_dev); + struct resource *r; + int chan; + + if (!dmaman_dev) + return -ENODEV; + + mutex_lock(&dmaman->lock); + chan = vc_dmaman_chan_alloc(dmaman, preferred_feature_set); + if (chan < 0) + goto out; + + r = platform_get_resource(pdev, IORESOURCE_IRQ, (unsigned int)chan); + if (!r) { + dev_err(dmaman_dev, "failed to get irq for DMA channel %d\n", + chan); + vc_dmaman_chan_free(dmaman, chan); + chan = -ENOENT; + goto out; + } + + *out_dma_base = BCM2708_DMA_CHANIO(dmaman->dma_base, chan); + *out_dma_irq = r->start; + dev_dbg(dmaman_dev, + "Legacy API allocated channel=%d, base=%p, irq=%i\n", + chan, *out_dma_base, *out_dma_irq); + +out: + mutex_unlock(&dmaman->lock); + + return chan; +} +EXPORT_SYMBOL_GPL(bcm_dma_chan_alloc); + +extern int bcm_dma_chan_free(int channel) +{ + struct vc_dmaman *dmaman = g_dmaman; + int rc; + + if (!dmaman_dev) + return -ENODEV; + + mutex_lock(&dmaman->lock); + rc = vc_dmaman_chan_free(dmaman, channel); + mutex_unlock(&dmaman->lock); + + return rc; +} +EXPORT_SYMBOL_GPL(bcm_dma_chan_free); + +static int bcm_dmaman_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct vc_dmaman *dmaman; + struct resource *r; + void __iomem *dma_base; + uint32_t val; + + if (!of_property_read_u32(dev->of_node, + "brcm,dma-channel-mask", &val)) + dmachans = val; + else if (dmachans == -1) + dmachans = DEFAULT_DMACHAN_BITMAP; + + dmaman = devm_kzalloc(dev, sizeof(*dmaman), GFP_KERNEL); + if (!dmaman) + return -ENOMEM; + + mutex_init(&dmaman->lock); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dma_base = devm_ioremap_resource(dev, r); + if (IS_ERR(dma_base)) + return PTR_ERR(dma_base); + + vc_dmaman_init(dmaman, dma_base, dmachans); + g_dmaman = dmaman; + dmaman_dev = dev; + + dev_info(dev, "DMA legacy API manager at %p, dmachans=0x%x\n", + dma_base, dmachans); + + return 0; +} + +static int bcm_dmaman_remove(struct platform_device *pdev) +{ + dmaman_dev = NULL; + + return 0; +} + +#else /* CONFIG_DMA_BCM2708_LEGACY */ + +static int bcm_dmaman_remove(struct platform_device *pdev) +{ + return 0; +} + +#endif /* CONFIG_DMA_BCM2708_LEGACY */ + +/* + * DMA engine + */ struct bcm2835_dmadev { struct dma_device ddev; @@ -709,7 +987,7 @@ static int bcm2835_dma_terminate_all(struct dma_chan *chan) return 0; } -#ifdef CONFIG_ARCH_BCM2835 +#ifndef CONFIG_DMA_BCM2708_LEGACY static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, int irq) { struct bcm2835_chan *c; @@ -787,7 +1065,7 @@ static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec, static int bcm2835_dma_probe(struct platform_device *pdev) { struct bcm2835_dmadev *od; -#ifdef CONFIG_ARCH_BCM2835 +#ifndef CONFIG_DMA_BCM2708_LEGACY struct resource *res; void __iomem *base; uint32_t chans_available; @@ -800,10 +1078,7 @@ static int bcm2835_dma_probe(struct platform_device *pdev) if (!pdev->dev.dma_mask) pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; - /* If CONFIG_ARCH_BCM2835 is selected, device tree is used */ - /* hence the difference between probing */ - -#ifndef CONFIG_ARCH_BCM2835 +#ifdef CONFIG_DMA_BCM2708_LEGACY rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (rc) @@ -815,6 +1090,10 @@ static int bcm2835_dma_probe(struct platform_device *pdev) if (!od) return -ENOMEM; + rc = bcm_dmaman_probe(pdev); + if (rc) + return rc; + pdev->dev.dma_parms = &od->dma_parms; dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); @@ -971,6 +1250,7 @@ static int bcm2835_dma_remove(struct platform_device *pdev) dma_async_device_unregister(&od->ddev); bcm2835_dma_free(od); + bcm_dmaman_remove(pdev); return 0; } @@ -985,9 +1265,30 @@ static struct platform_driver bcm2835_dma_driver = { }, }; -module_platform_driver(bcm2835_dma_driver); +static int bcm2835_init(void) +{ + return platform_driver_register(&bcm2835_dma_driver); +} + +static void bcm2835_exit(void) +{ + platform_driver_unregister(&bcm2835_dma_driver); +} + +/* + * Load after serial driver (arch_initcall) so we see the messages if it fails, + * but before drivers (module_init) that need a DMA channel. + */ +subsys_initcall(bcm2835_init); +module_exit(bcm2835_exit); module_param(dma_debug, uint, 0644); +#ifdef CONFIG_DMA_BCM2708_LEGACY +/* Keep backward compatibility: dma.dmachans= */ +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "dma." +module_param(dmachans, int, 0644); +#endif MODULE_ALIAS("platform:bcm2835-dma"); MODULE_DESCRIPTION("BCM2835 DMA engine driver"); MODULE_AUTHOR("Florian Meier "); diff --git a/include/linux/platform_data/dma-bcm2708.h b/include/linux/platform_data/dma-bcm2708.h new file mode 100644 index 0000000000000..2310e347c6a19 --- /dev/null +++ b/include/linux/platform_data/dma-bcm2708.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2010 Broadcom + * + * 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. + */ + +#ifndef _PLAT_BCM2708_DMA_H +#define _PLAT_BCM2708_DMA_H + +/* DMA CS Control and Status bits */ +#define BCM2708_DMA_ACTIVE BIT(0) +#define BCM2708_DMA_INT BIT(2) +#define BCM2708_DMA_ISPAUSED BIT(4) /* Pause requested or not active */ +#define BCM2708_DMA_ISHELD BIT(5) /* Is held by DREQ flow control */ +#define BCM2708_DMA_ERR BIT(8) +#define BCM2708_DMA_ABORT BIT(30) /* stop current CB, go to next, WO */ +#define BCM2708_DMA_RESET BIT(31) /* WO, self clearing */ + +/* DMA control block "info" field bits */ +#define BCM2708_DMA_INT_EN BIT(0) +#define BCM2708_DMA_TDMODE BIT(1) +#define BCM2708_DMA_WAIT_RESP BIT(3) +#define BCM2708_DMA_D_INC BIT(4) +#define BCM2708_DMA_D_WIDTH BIT(5) +#define BCM2708_DMA_D_DREQ BIT(6) +#define BCM2708_DMA_S_INC BIT(8) +#define BCM2708_DMA_S_WIDTH BIT(9) +#define BCM2708_DMA_S_DREQ BIT(10) + +#define BCM2708_DMA_BURST(x) (((x) & 0xf) << 12) +#define BCM2708_DMA_PER_MAP(x) ((x) << 16) +#define BCM2708_DMA_WAITS(x) (((x) & 0x1f) << 21) + +#define BCM2708_DMA_DREQ_EMMC 11 +#define BCM2708_DMA_DREQ_SDHOST 13 + +#define BCM2708_DMA_CS 0x00 /* Control and Status */ +#define BCM2708_DMA_ADDR 0x04 +/* the current control block appears in the following registers - read only */ +#define BCM2708_DMA_INFO 0x08 +#define BCM2708_DMA_SOURCE_AD 0x0c +#define BCM2708_DMA_DEST_AD 0x10 +#define BCM2708_DMA_NEXTCB 0x1C +#define BCM2708_DMA_DEBUG 0x20 + +#define BCM2708_DMA4_CS (BCM2708_DMA_CHAN(4) + BCM2708_DMA_CS) +#define BCM2708_DMA4_ADDR (BCM2708_DMA_CHAN(4) + BCM2708_DMA_ADDR) + +#define BCM2708_DMA_TDMODE_LEN(w, h) ((h) << 16 | (w)) + +/* When listing features we can ask for when allocating DMA channels give + those with higher priority smaller ordinal numbers */ +#define BCM_DMA_FEATURE_FAST_ORD 0 +#define BCM_DMA_FEATURE_BULK_ORD 1 +#define BCM_DMA_FEATURE_NORMAL_ORD 2 +#define BCM_DMA_FEATURE_LITE_ORD 3 +#define BCM_DMA_FEATURE_FAST BIT(BCM_DMA_FEATURE_FAST_ORD) +#define BCM_DMA_FEATURE_BULK BIT(BCM_DMA_FEATURE_BULK_ORD) +#define BCM_DMA_FEATURE_NORMAL BIT(BCM_DMA_FEATURE_NORMAL_ORD) +#define BCM_DMA_FEATURE_LITE BIT(BCM_DMA_FEATURE_LITE_ORD) +#define BCM_DMA_FEATURE_COUNT 4 + +struct bcm2708_dma_cb { + unsigned long info; + unsigned long src; + unsigned long dst; + unsigned long length; + unsigned long stride; + unsigned long next; + unsigned long pad[2]; +}; + +struct scatterlist; + +#ifdef CONFIG_DMA_BCM2708_LEGACY + +int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len); +void bcm_dma_start(void __iomem *dma_chan_base, dma_addr_t control_block); +void bcm_dma_wait_idle(void __iomem *dma_chan_base); +bool bcm_dma_is_busy(void __iomem *dma_chan_base); +int bcm_dma_abort(void __iomem *dma_chan_base); + +/* return channel no or -ve error */ +int bcm_dma_chan_alloc(unsigned preferred_feature_set, + void __iomem **out_dma_base, int *out_dma_irq); +int bcm_dma_chan_free(int channel); + +#else /* CONFIG_DMA_BCM2708_LEGACY */ + +static inline int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, + int sg_len) +{ + return 0; +} + +static inline void bcm_dma_start(void __iomem *dma_chan_base, + dma_addr_t control_block) { } + +static inline void bcm_dma_wait_idle(void __iomem *dma_chan_base) { } + +static inline bool bcm_dma_is_busy(void __iomem *dma_chan_base) +{ + return false; +} + +static inline int bcm_dma_abort(void __iomem *dma_chan_base) +{ + return -EINVAL; +} + +static inline int bcm_dma_chan_alloc(unsigned preferred_feature_set, + void __iomem **out_dma_base, + int *out_dma_irq) +{ + return -EINVAL; +} + +static inline int bcm_dma_chan_free(int channel) +{ + return -EINVAL; +} + +#endif /* CONFIG_DMA_BCM2708_LEGACY */ + +#endif /* _PLAT_BCM2708_DMA_H */