Support for the Timberdale FPGA internal DMA engine
This driver gives other drivers the possibility to start DMA directly
between IP block inside the FPGA and the main memory
Signed-off-by: Richard R?jfors <[email protected]>
---
Index: linux-2.6.30-rc7/drivers/mfd/Kconfig
===================================================================
--- linux-2.6.30-rc7/drivers/mfd/Kconfig (revision 861)
+++ linux-2.6.30-rc7/drivers/mfd/Kconfig (working copy)
@@ -241,6 +241,13 @@
Say yes here if you want to include support GPIO for pins on
the PCF50633 chip.
+config MFD_TIMBERDALE_DMA
+ tristate "Support for timberdale DMA"
+ depends on MFD_TIMBERDALE
+ ---help---
+ Add support the DMA block inside the timberdale FPGA. This to be able
+ to do DMA transfers directly to some of the blocks inside the FPGA
+
endmenu
menu "Multimedia Capabilities Port drivers"
Index: linux-2.6.30-rc7/drivers/mfd/timbdma.c
===================================================================
--- linux-2.6.30-rc7/drivers/mfd/timbdma.c (revision 0)
+++ linux-2.6.30-rc7/drivers/mfd/timbdma.c (revision 864)
@@ -0,0 +1,302 @@
+/*
+ * timbdma.c timberdale FPGA DMA driver
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA DMA engine
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/timbdma.h>
+
+static struct timbdma_dev *self_g;
+
+static irqreturn_t timbdma_handleinterrupt(int irq, void *devid)
+{
+ struct timbdma_dev *dev = (struct timbdma_dev *)devid;
+ int ipr;
+ int i;
+
+ ipr = ioread32(dev->membase + timbdma_ctrlmap_TIMBPEND);
+
+ /* ack */
+ iowrite32(ipr, dev->membase + timbdma_ctrlmap_TIMBSTATUS);
+
+ /* call the callbacks */
+ for (i = 0; i < DMA_IRQS; i++) {
+ int mask = 1 << i;
+ if ((ipr & mask) && dev->callbacks[i])
+ dev->callbacks[i](mask, dev->callback_data[i]);
+ }
+
+ if (ipr)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+
+void timb_start_dma(u32 flag, unsigned long buf, int len, int bytes_per_row)
+{
+ int i;
+ unsigned long irqflags;
+ struct timbdma_dev *dev = self_g;
+
+ spin_lock_irqsave(&dev->lock, irqflags);
+
+ /* now enable the DMA transfer */
+ for (i = 0; i < DMA_IRQS; i++)
+ if (flag & (1 << i)) {
+ u32 offset = i / 2 * 0x40;
+
+ if (!(i % 2)) {
+ /* RX */
+ /* bytes per row */
+ iowrite32(bytes_per_row, dev->membase + offset +
+ timbdma_dmacfg_BPERROW);
+ /* address high */
+ iowrite32(0, dev->membase + offset +
+ timbdma_dmacfg_RXSTARTH);
+ /* address low */
+ iowrite32(buf, dev->membase + offset +
+ timbdma_dmacfg_RXSTARTL);
+ /* Length */
+ iowrite32(len, dev->membase + offset +
+ timbdma_dmacfg_RXLENGTH);
+ /* Clear rx sw read pointer */
+ iowrite32(0, dev->membase + offset +
+ timbdma_dmacfg_RXSWRP);
+ /* enable the transfer */
+ iowrite32(1, dev->membase + offset +
+ timbdma_dmacfg_RXENABLE);
+ } else {
+ /* TX */
+ /* address high */
+ iowrite32(0, dev->membase + offset +
+ timbdma_dmacfg_TXSTARTH);
+ /* address low */
+ iowrite32(buf, dev->membase + offset +
+ timbdma_dmacfg_TXSTARTL);
+ /* Length */
+ iowrite32(len, dev->membase + offset +
+ timbdma_dmacfg_TXLENGTH);
+ /* Set tx sw write pointer */
+ iowrite32(len, dev->membase + offset +
+ timbdma_dmacfg_TXSWWP);
+ }
+
+ /* only allow one bit in the flag field */
+ break;
+ }
+ spin_unlock_irqrestore(&dev->lock, irqflags);
+}
+EXPORT_SYMBOL(timb_start_dma);
+
+void *timb_stop_dma(u32 flags)
+{
+ int i;
+ unsigned long irqflags;
+ struct timbdma_dev *dev = self_g;
+ void *result = 0;
+
+ spin_lock_irqsave(&dev->lock, irqflags);
+
+ /* now disable the DMA transfers */
+ for (i = 0; i < DMA_IRQS; i++)
+ if (flags & (1 << i)) {
+ /*
+ RX enable registers are located at:
+ 0x14
+ 0x54
+ 0x94
+
+ TX SW pointer registers are located at:
+ 0x24
+ 0x64
+ */
+ u32 offset = i / 2 * 0x40;
+ u32 result_offset = offset;
+ if (!(i % 2)) {
+ /* even -> RX enable */
+ offset += timbdma_dmacfg_RXENABLE;
+ result_offset += timbdma_dmacfg_RXFPGAWP;
+ } else {
+ /* odd -> TX SW pointer reg */
+ offset += timbdma_dmacfg_TXSWWP;
+ result_offset = timbdma_dmacfg_TXFPGARP;
+ }
+
+ iowrite32(0, dev->membase + offset);
+ /* check how far the FPGA has written/read */
+ result = (void *)ioread32(dev->membase + result_offset);
+ }
+
+ /* ack any pending IRQs */
+ iowrite32(flags, dev->membase + timbdma_ctrlmap_TIMBSTATUS);
+
+ spin_unlock_irqrestore(&dev->lock, irqflags);
+
+ return result;
+}
+EXPORT_SYMBOL(timb_stop_dma);
+
+void timb_set_dma_interruptcb(u32 flags, timbdma_interruptcb icb, void *data)
+{
+ int i;
+ unsigned long irqflags;
+ struct timbdma_dev *dev = self_g;
+ u32 ier;
+
+ spin_lock_irqsave(&dev->lock, irqflags);
+
+ for (i = 0; i < DMA_IRQS; i++)
+ if (flags & (1 << i)) {
+ dev->callbacks[i] = icb;
+ dev->callback_data[i] = data;
+ }
+
+ /* Ack any pending IRQ */
+ iowrite32(flags, dev->membase + timbdma_ctrlmap_TIMBSTATUS);
+
+ /* if a null callback is given -> clear interrupt, else -> enable */
+ ier = ioread32(dev->membase + timbdma_ctrlmap_TIMBENABLE);
+ if (icb != NULL)
+ ier |= flags;
+ else
+ ier &= ~flags;
+ iowrite32(ier, dev->membase + timbdma_ctrlmap_TIMBENABLE);
+
+ spin_unlock_irqrestore(&dev->lock, irqflags);
+}
+EXPORT_SYMBOL(timb_set_dma_interruptcb);
+
+static int timbdma_probe(struct platform_device *dev)
+{
+ int err, irq;
+ struct timbdma_dev *self;
+ struct resource *iomem;
+
+ iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!iomem) {
+ err = -EINVAL;
+ goto err_mem;
+ }
+
+ self = kzalloc(sizeof(*self), GFP_KERNEL);
+ if (!self) {
+ err = -EINVAL;
+ goto err_mem;
+ }
+
+ spin_lock_init(&self->lock);
+
+ if (!request_mem_region(iomem->start,
+ resource_size(iomem), "timb-dma")) {
+ err = -EBUSY;
+ goto err_request;
+ }
+
+ self->membase = ioremap(iomem->start, resource_size(iomem));
+ if (!self->membase) {
+ printk(KERN_ERR "timbdma: Failed to remap I/O memory\n");
+ err = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ /* register interrupt */
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0) {
+ err = irq;
+ goto err_get_irq;
+ }
+
+ /* request IRQ */
+ err = request_irq(irq, timbdma_handleinterrupt, IRQF_SHARED,
+ "timb-dma", self);
+ if (err) {
+ printk(KERN_ERR "timbdma: Failed to request IRQ\n");
+ goto err_get_irq;
+ }
+
+ platform_set_drvdata(dev, self);
+
+ /* assign the global pointer */
+ self_g = self;
+
+ return 0;
+
+err_get_irq:
+ iounmap(self->membase);
+err_ioremap:
+ release_mem_region(iomem->start, resource_size(iomem));
+err_request:
+ kfree(self);
+err_mem:
+ printk(KERN_ERR "timberdale: Failed to register Timberdale DMA: %d\n",
+ err);
+
+ return err;
+}
+
+static int timbdma_remove(struct platform_device *dev)
+{
+ struct timbdma_dev *self = platform_get_drvdata(dev);
+ struct resource *iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+
+ free_irq(platform_get_irq(dev, 0), self);
+ iounmap(self->membase);
+ release_mem_region(iomem->start, resource_size(iomem));
+ kfree(self);
+ self_g = NULL;
+ return 0;
+}
+
+static struct platform_driver timbdma_platform_driver = {
+ .driver = {
+ .name = "timb-dma",
+ .owner = THIS_MODULE,
+ },
+ .probe = timbdma_probe,
+ .remove = timbdma_remove,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int __init timbdma_init(void)
+{
+ self_g = NULL;
+ return platform_driver_register(&timbdma_platform_driver);
+}
+
+static void __exit timbdma_exit(void)
+{
+ platform_driver_unregister(&timbdma_platform_driver);
+}
+
+module_init(timbdma_init);
+module_exit(timbdma_exit);
+
+MODULE_DESCRIPTION("Timberdale DMA driver");
+MODULE_AUTHOR("Mocean Laboratories <[email protected]>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:timb-dma");
+
Index: linux-2.6.30-rc7/drivers/mfd/Makefile
===================================================================
--- linux-2.6.30-rc7/drivers/mfd/Makefile (revision 861)
+++ linux-2.6.30-rc7/drivers/mfd/Makefile (working copy)
@@ -40,4 +40,6 @@
obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o
obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
-obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
\ No newline at end of file
+obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
+
+obj-$(CONFIG_MFD_TIMBERDALE_DMA) += timbdma.o
Index: linux-2.6.30-rc7/include/linux/mfd/timbdma.h
===================================================================
--- linux-2.6.30-rc7/include/linux/mfd/timbdma.h (revision 0)
+++ linux-2.6.30-rc7/include/linux/mfd/timbdma.h (revision 889)
@@ -0,0 +1,76 @@
+/*
+ * timbdma.h timberdale FPGA DMA driver defines
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA DMA engine
+ */
+
+#ifndef _TIMBDMA_H
+#define _TIMBDMA_H
+
+#include <linux/spinlock.h>
+
+
+#define DMA_IRQ_UART_RX 0x01
+#define DMA_IRQ_UART_TX 0x02
+#define DMA_IRQ_MLB_RX 0x04
+#define DMA_IRQ_MLB_TX 0x08
+#define DMA_IRQ_VIDEO_RX 0x10
+#define DMA_IRQ_VIDEO_DROP 0x20
+#define DMA_IRQS 6
+
+
+typedef int (*timbdma_interruptcb)(u32 flag, void *data);
+
+#define timbdma_ctrlmap_DMACFGBTUART 0x000000
+#define timbdma_ctrlmap_DMACFGMLBSY 0x000040
+#define timbdma_ctrlmap_DMACFGVIDEO 0x000080
+#define timbdma_ctrlmap_TIMBSTATUS 0x080000
+#define timbdma_ctrlmap_TIMBPEND 0x080004
+#define timbdma_ctrlmap_TIMBENABLE 0x080008
+#define timbdma_ctrlmap_VIDEOBUFFER 0x200000
+
+#define timbdma_dmacfg_RXSTARTH 0x00
+#define timbdma_dmacfg_RXSTARTL 0x04
+#define timbdma_dmacfg_RXLENGTH 0x08
+#define timbdma_dmacfg_RXFPGAWP 0x0C
+#define timbdma_dmacfg_RXSWRP 0x10
+#define timbdma_dmacfg_RXENABLE 0x14
+#define timbdma_dmacfg_TXSTARTH 0x18
+#define timbdma_dmacfg_TXSTARTL 0x1C
+#define timbdma_dmacfg_TXLENGTH 0x20
+#define timbdma_dmacfg_TXSWWP 0x24
+#define timbdma_dmacfg_TXFPGARP 0x28
+#define timbdma_dmacfg_TXBEFINT 0x2C
+#define timbdma_dmacfg_BPERROW 0x30
+
+struct timbdma_dev {
+ void __iomem *membase;
+ timbdma_interruptcb callbacks[DMA_IRQS];
+ void *callback_data[DMA_IRQS];
+ spinlock_t lock; /* mutual exclusion */
+};
+
+void timb_start_dma(u32 flag, unsigned long buf, int len, int bytes_per_row);
+
+void *timb_stop_dma(u32 flags);
+
+void timb_set_dma_interruptcb(u32 flags, timbdma_interruptcb icb, void *data);
+
+#endif /* _TIMBDMA_H */
+