[PATCH 4/9] MFD: Adding Timberdale DMA driver

From: Richard Röjfors
Date: Fri Jun 05 2009 - 09:42:13 EST


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 <richard.rojfors.ext@xxxxxxxxxxxxxxx>
---
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 <info@xxxxxxxxxxxxxxx>");
+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 */
+
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/