diff --git a/arch/arm/boot/dts/bcm2708_common.dtsi b/arch/arm/boot/dts/bcm2708_common.dtsi index ff70c5849f29b4..065a4244bc7879 100644 --- a/arch/arm/boot/dts/bcm2708_common.dtsi +++ b/arch/arm/boot/dts/bcm2708_common.dtsi @@ -10,7 +10,23 @@ dma: dma@7e007000 { compatible = "brcm,bcm2835-dma"; + reg = <0x7e007000 0xf00>; + interrupts = <1 16>, + <1 17>, + <1 18>, + <1 19>, + <1 20>, + <1 21>, + <1 22>, + <1 23>, + <1 24>, + <1 25>, + <1 26>, + <1 27>, + <1 28>; + #dma-cells = <1>; + brcm,dma-channel-mask = <0x7f35>; }; intc: interrupt-controller { diff --git a/arch/arm/mach-bcm2708/Makefile b/arch/arm/mach-bcm2708/Makefile index 21e352166d9ea3..454408cbeade8a 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/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c index b848e4a1aa67cb..486e090a8f3093 100644 --- a/arch/arm/mach-bcm2708/bcm2708.c +++ b/arch/arm/mach-bcm2708/bcm2708.c @@ -55,7 +55,6 @@ #include #include -#include #include #include @@ -248,24 +247,71 @@ static struct amba_device *amba_devs[] __initdata = { &uart0_device, }; -static struct resource bcm2708_dmaman_resources[] = { +static struct resource bcm2708_dmaengine_resources[] = { { - .start = DMA_BASE, - .end = DMA_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM, - } -}; - -static struct platform_device bcm2708_dmaman_device = { - .name = BCM_DMAMAN_DRIVER_NAME, - .id = 0, /* first bcm2708_dma */ - .resource = bcm2708_dmaman_resources, - .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), + .start = DMA_BASE, + .end = DMA_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_DMA0, + .end = IRQ_DMA0, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA1, + .end = IRQ_DMA1, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA2, + .end = IRQ_DMA2, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA3, + .end = IRQ_DMA3, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA4, + .end = IRQ_DMA4, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA5, + .end = IRQ_DMA5, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA6, + .end = IRQ_DMA6, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA7, + .end = IRQ_DMA7, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA8, + .end = IRQ_DMA8, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA9, + .end = IRQ_DMA9, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA10, + .end = IRQ_DMA10, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA11, + .end = IRQ_DMA11, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA12, + .end = IRQ_DMA12, + .flags = IORESOURCE_IRQ, + } }; static struct platform_device bcm2708_dmaengine_device = { .name = "bcm2708-dmaengine", .id = -1, + .resource = bcm2708_dmaengine_resources, + .num_resources = ARRAY_SIZE(bcm2708_dmaengine_resources), }; #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) @@ -858,7 +904,6 @@ void __init bcm2708_init(void) bcm2708_init_clocks(); bcm2708_dt_init(); - bcm_register_device(&bcm2708_dmaman_device); bcm_register_device_dt(&bcm2708_dmaengine_device); bcm_register_device(&bcm2708_vcio_device); #ifdef CONFIG_BCM2708_GPIO diff --git a/arch/arm/mach-bcm2708/dma.c b/arch/arm/mach-bcm2708/dma.c deleted file mode 100644 index a5e58d1f847564..00000000000000 --- a/arch/arm/mach-bcm2708/dma.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * linux/arch/arm/mach-bcm2708/dma.c - * - * 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. - */ - -#include -#include -#include -#include -#include - -#include -#include - -/*****************************************************************************\ - * * - * Configuration * - * * -\*****************************************************************************/ - -#define CACHE_LINE_MASK 31 -#define DRIVER_NAME BCM_DMAMAN_DRIVER_NAME -#define DEFAULT_DMACHAN_BITMAP 0x10 /* channel 4 only */ - -/* 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))) - - -/*****************************************************************************\ - * * - * DMA Auxilliary 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); -} - -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_start); - -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 * - * * -\*****************************************************************************/ - -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 */ -}; - -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; /* chans 2 & 3 */ - dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* chan 0 */ - dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* chans 1 to 7 */ - dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* chans 8 to 14 */ -} - -static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, - unsigned preferred_feature_set) -{ - u32 chans; - 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) { - int chan = 0; - /* 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; - } else - return -ENOMEM; -} - -static int vc_dmaman_chan_free(struct vc_dmaman *dmaman, int chan) -{ - if (chan < 0) - return -EINVAL; - else if ((1 << chan) & dmaman->chan_available) - return -EIDRM; - else { - dmaman->chan_available |= (1 << chan); - return 0; - } -} - -/*****************************************************************************\ - * * - * DMA IRQs * - * * -\*****************************************************************************/ - -static unsigned char bcm_dma_irqs[] = { - IRQ_DMA0, - IRQ_DMA1, - IRQ_DMA2, - IRQ_DMA3, - IRQ_DMA4, - IRQ_DMA5, - IRQ_DMA6, - IRQ_DMA7, - IRQ_DMA8, - IRQ_DMA9, - IRQ_DMA10, - IRQ_DMA11, - IRQ_DMA12 -}; - - -/***************************************************************************** \ - * * - * DMA Manager Monitor * - * * -\*****************************************************************************/ - -static struct device *dmaman_dev; /* we assume there's only one! */ - -extern int bcm_dma_chan_alloc(unsigned preferred_feature_set, - void __iomem **out_dma_base, int *out_dma_irq) -{ - if (!dmaman_dev) - return -ENODEV; - else { - struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); - int rc; - - device_lock(dmaman_dev); - rc = vc_dmaman_chan_alloc(dmaman, preferred_feature_set); - if (rc >= 0) { - *out_dma_base = BCM2708_DMA_CHANIO(dmaman->dma_base, - rc); - *out_dma_irq = bcm_dma_irqs[rc]; - } - device_unlock(dmaman_dev); - - return rc; - } -} -EXPORT_SYMBOL_GPL(bcm_dma_chan_alloc); - -extern int bcm_dma_chan_free(int channel) -{ - if (dmaman_dev) { - struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); - int rc; - - device_lock(dmaman_dev); - rc = vc_dmaman_chan_free(dmaman, channel); - device_unlock(dmaman_dev); - - return rc; - } else - return -ENODEV; -} -EXPORT_SYMBOL_GPL(bcm_dma_chan_free); - -static int dev_dmaman_register(const char *dev_name, struct device *dev) -{ - int rc = dmaman_dev ? -EINVAL : 0; - dmaman_dev = dev; - return rc; -} - -static void dev_dmaman_deregister(const char *dev_name, struct device *dev) -{ - dmaman_dev = NULL; -} - -/*****************************************************************************\ - * * - * DMA Device * - * * -\*****************************************************************************/ - -static int dmachans = -1; /* module parameter */ - -static int bcm_dmaman_probe(struct platform_device *pdev) -{ - int ret = 0; - struct vc_dmaman *dmaman; - struct resource *dma_res = NULL; - void __iomem *dma_base = NULL; - int have_dma_region = 0; - - dmaman = kzalloc(sizeof(*dmaman), GFP_KERNEL); - if (NULL == dmaman) { - printk(KERN_ERR DRIVER_NAME ": failed to allocate " - "DMA management memory\n"); - ret = -ENOMEM; - } else { - - dma_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (dma_res == NULL) { - printk(KERN_ERR DRIVER_NAME ": failed to obtain memory " - "resource\n"); - ret = -ENODEV; - } else if (!request_mem_region(dma_res->start, - resource_size(dma_res), - DRIVER_NAME)) { - dev_err(&pdev->dev, "cannot obtain DMA region\n"); - ret = -EBUSY; - } else { - have_dma_region = 1; - dma_base = ioremap(dma_res->start, - resource_size(dma_res)); - if (!dma_base) { - dev_err(&pdev->dev, "cannot map DMA region\n"); - ret = -ENOMEM; - } else { - /* use module parameter if one was provided */ - if (dmachans > 0) - vc_dmaman_init(dmaman, dma_base, - dmachans); - else - vc_dmaman_init(dmaman, dma_base, - DEFAULT_DMACHAN_BITMAP); - - platform_set_drvdata(pdev, dmaman); - dev_dmaman_register(DRIVER_NAME, &pdev->dev); - - printk(KERN_INFO DRIVER_NAME ": DMA manager " - "at %p\n", dma_base); - } - } - } - if (ret != 0) { - if (dma_base) - iounmap(dma_base); - if (dma_res && have_dma_region) - release_mem_region(dma_res->start, - resource_size(dma_res)); - if (dmaman) - kfree(dmaman); - } - return ret; -} - -static int bcm_dmaman_remove(struct platform_device *pdev) -{ - struct vc_dmaman *dmaman = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); - dev_dmaman_deregister(DRIVER_NAME, &pdev->dev); - kfree(dmaman); - - return 0; -} - -static struct platform_driver bcm_dmaman_driver = { - .probe = bcm_dmaman_probe, - .remove = bcm_dmaman_remove, - - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -/*****************************************************************************\ - * * - * Driver init/exit * - * * -\*****************************************************************************/ - -static int __init bcm_dmaman_drv_init(void) -{ - int ret; - - ret = platform_driver_register(&bcm_dmaman_driver); - if (ret != 0) { - printk(KERN_ERR DRIVER_NAME ": failed to register " - "on platform\n"); - } - - return ret; -} - -static void __exit bcm_dmaman_drv_exit(void) -{ - platform_driver_unregister(&bcm_dmaman_driver); -} - -module_init(bcm_dmaman_drv_init); -module_exit(bcm_dmaman_drv_exit); - -module_param(dmachans, int, 0644); - -MODULE_AUTHOR("Gray Girling "); -MODULE_DESCRIPTION("DMA channel manager driver"); -MODULE_LICENSE("GPL"); - -MODULE_PARM_DESC(dmachans, "Bitmap of DMA channels available to the ARM"); diff --git a/arch/arm/mach-bcm2708/include/mach/dma.h b/arch/arm/mach-bcm2708/include/mach/dma.h index d03e7b5a1f6b0b..d826705e1574e2 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 2a803bb6bf45d9..f07c38b77aba5b 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/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c index 56d16a4b575e0b..dcd3b47b175217 100644 --- a/arch/arm/mach-bcm2709/bcm2709.c +++ b/arch/arm/mach-bcm2709/bcm2709.c @@ -56,7 +56,6 @@ #include #include -#include #include #include @@ -258,24 +257,71 @@ static struct amba_device *amba_devs[] __initdata = { &uart0_device, }; -static struct resource bcm2708_dmaman_resources[] = { +static struct resource bcm2708_dmaengine_resources[] = { { - .start = DMA_BASE, - .end = DMA_BASE + SZ_4K - 1, - .flags = IORESOURCE_MEM, - } -}; - -static struct platform_device bcm2708_dmaman_device = { - .name = BCM_DMAMAN_DRIVER_NAME, - .id = 0, /* first bcm2708_dma */ - .resource = bcm2708_dmaman_resources, - .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), + .start = DMA_BASE, + .end = DMA_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_DMA0, + .end = IRQ_DMA0, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA1, + .end = IRQ_DMA1, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA2, + .end = IRQ_DMA2, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA3, + .end = IRQ_DMA3, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA4, + .end = IRQ_DMA4, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA5, + .end = IRQ_DMA5, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA6, + .end = IRQ_DMA6, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA7, + .end = IRQ_DMA7, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA8, + .end = IRQ_DMA8, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA9, + .end = IRQ_DMA9, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA10, + .end = IRQ_DMA10, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA11, + .end = IRQ_DMA11, + .flags = IORESOURCE_IRQ, + }, { + .start = IRQ_DMA12, + .end = IRQ_DMA12, + .flags = IORESOURCE_IRQ, + } }; static struct platform_device bcm2708_dmaengine_device = { .name = "bcm2708-dmaengine", .id = -1, + .resource = bcm2708_dmaengine_resources, + .num_resources = ARRAY_SIZE(bcm2708_dmaengine_resources), }; #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) @@ -879,7 +925,6 @@ void __init bcm2709_init(void) bcm2709_init_clocks(); bcm2709_dt_init(); - bcm_register_device(&bcm2708_dmaman_device); bcm_register_device_dt(&bcm2708_dmaengine_device); bcm_register_device(&bcm2708_vcio_device); #ifdef CONFIG_BCM2708_GPIO diff --git a/arch/arm/mach-bcm2709/dma.c b/arch/arm/mach-bcm2709/dma.c deleted file mode 100644 index a5e58d1f847564..00000000000000 --- a/arch/arm/mach-bcm2709/dma.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * linux/arch/arm/mach-bcm2708/dma.c - * - * 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. - */ - -#include -#include -#include -#include -#include - -#include -#include - -/*****************************************************************************\ - * * - * Configuration * - * * -\*****************************************************************************/ - -#define CACHE_LINE_MASK 31 -#define DRIVER_NAME BCM_DMAMAN_DRIVER_NAME -#define DEFAULT_DMACHAN_BITMAP 0x10 /* channel 4 only */ - -/* 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))) - - -/*****************************************************************************\ - * * - * DMA Auxilliary 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); -} - -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_start); - -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 * - * * -\*****************************************************************************/ - -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 */ -}; - -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; /* chans 2 & 3 */ - dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* chan 0 */ - dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* chans 1 to 7 */ - dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* chans 8 to 14 */ -} - -static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, - unsigned preferred_feature_set) -{ - u32 chans; - 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) { - int chan = 0; - /* 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; - } else - return -ENOMEM; -} - -static int vc_dmaman_chan_free(struct vc_dmaman *dmaman, int chan) -{ - if (chan < 0) - return -EINVAL; - else if ((1 << chan) & dmaman->chan_available) - return -EIDRM; - else { - dmaman->chan_available |= (1 << chan); - return 0; - } -} - -/*****************************************************************************\ - * * - * DMA IRQs * - * * -\*****************************************************************************/ - -static unsigned char bcm_dma_irqs[] = { - IRQ_DMA0, - IRQ_DMA1, - IRQ_DMA2, - IRQ_DMA3, - IRQ_DMA4, - IRQ_DMA5, - IRQ_DMA6, - IRQ_DMA7, - IRQ_DMA8, - IRQ_DMA9, - IRQ_DMA10, - IRQ_DMA11, - IRQ_DMA12 -}; - - -/***************************************************************************** \ - * * - * DMA Manager Monitor * - * * -\*****************************************************************************/ - -static struct device *dmaman_dev; /* we assume there's only one! */ - -extern int bcm_dma_chan_alloc(unsigned preferred_feature_set, - void __iomem **out_dma_base, int *out_dma_irq) -{ - if (!dmaman_dev) - return -ENODEV; - else { - struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); - int rc; - - device_lock(dmaman_dev); - rc = vc_dmaman_chan_alloc(dmaman, preferred_feature_set); - if (rc >= 0) { - *out_dma_base = BCM2708_DMA_CHANIO(dmaman->dma_base, - rc); - *out_dma_irq = bcm_dma_irqs[rc]; - } - device_unlock(dmaman_dev); - - return rc; - } -} -EXPORT_SYMBOL_GPL(bcm_dma_chan_alloc); - -extern int bcm_dma_chan_free(int channel) -{ - if (dmaman_dev) { - struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); - int rc; - - device_lock(dmaman_dev); - rc = vc_dmaman_chan_free(dmaman, channel); - device_unlock(dmaman_dev); - - return rc; - } else - return -ENODEV; -} -EXPORT_SYMBOL_GPL(bcm_dma_chan_free); - -static int dev_dmaman_register(const char *dev_name, struct device *dev) -{ - int rc = dmaman_dev ? -EINVAL : 0; - dmaman_dev = dev; - return rc; -} - -static void dev_dmaman_deregister(const char *dev_name, struct device *dev) -{ - dmaman_dev = NULL; -} - -/*****************************************************************************\ - * * - * DMA Device * - * * -\*****************************************************************************/ - -static int dmachans = -1; /* module parameter */ - -static int bcm_dmaman_probe(struct platform_device *pdev) -{ - int ret = 0; - struct vc_dmaman *dmaman; - struct resource *dma_res = NULL; - void __iomem *dma_base = NULL; - int have_dma_region = 0; - - dmaman = kzalloc(sizeof(*dmaman), GFP_KERNEL); - if (NULL == dmaman) { - printk(KERN_ERR DRIVER_NAME ": failed to allocate " - "DMA management memory\n"); - ret = -ENOMEM; - } else { - - dma_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (dma_res == NULL) { - printk(KERN_ERR DRIVER_NAME ": failed to obtain memory " - "resource\n"); - ret = -ENODEV; - } else if (!request_mem_region(dma_res->start, - resource_size(dma_res), - DRIVER_NAME)) { - dev_err(&pdev->dev, "cannot obtain DMA region\n"); - ret = -EBUSY; - } else { - have_dma_region = 1; - dma_base = ioremap(dma_res->start, - resource_size(dma_res)); - if (!dma_base) { - dev_err(&pdev->dev, "cannot map DMA region\n"); - ret = -ENOMEM; - } else { - /* use module parameter if one was provided */ - if (dmachans > 0) - vc_dmaman_init(dmaman, dma_base, - dmachans); - else - vc_dmaman_init(dmaman, dma_base, - DEFAULT_DMACHAN_BITMAP); - - platform_set_drvdata(pdev, dmaman); - dev_dmaman_register(DRIVER_NAME, &pdev->dev); - - printk(KERN_INFO DRIVER_NAME ": DMA manager " - "at %p\n", dma_base); - } - } - } - if (ret != 0) { - if (dma_base) - iounmap(dma_base); - if (dma_res && have_dma_region) - release_mem_region(dma_res->start, - resource_size(dma_res)); - if (dmaman) - kfree(dmaman); - } - return ret; -} - -static int bcm_dmaman_remove(struct platform_device *pdev) -{ - struct vc_dmaman *dmaman = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); - dev_dmaman_deregister(DRIVER_NAME, &pdev->dev); - kfree(dmaman); - - return 0; -} - -static struct platform_driver bcm_dmaman_driver = { - .probe = bcm_dmaman_probe, - .remove = bcm_dmaman_remove, - - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -/*****************************************************************************\ - * * - * Driver init/exit * - * * -\*****************************************************************************/ - -static int __init bcm_dmaman_drv_init(void) -{ - int ret; - - ret = platform_driver_register(&bcm_dmaman_driver); - if (ret != 0) { - printk(KERN_ERR DRIVER_NAME ": failed to register " - "on platform\n"); - } - - return ret; -} - -static void __exit bcm_dmaman_drv_exit(void) -{ - platform_driver_unregister(&bcm_dmaman_driver); -} - -module_init(bcm_dmaman_drv_init); -module_exit(bcm_dmaman_drv_exit); - -module_param(dmachans, int, 0644); - -MODULE_AUTHOR("Gray Girling "); -MODULE_DESCRIPTION("DMA channel manager driver"); -MODULE_LICENSE("GPL"); - -MODULE_PARM_DESC(dmachans, "Bitmap of DMA channels available to the ARM"); diff --git a/arch/arm/mach-bcm2709/include/mach/dma.h b/arch/arm/mach-bcm2709/include/mach/dma.h index d03e7b5a1f6b0b..d826705e1574e2 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 2395f637f7d46c..41097c744a6a95 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 8182b1622cc090..937fd60c509226 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 00000000000000..2310e347c6a192 --- /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 */