From: Stefan Roese <[email protected]>

This patch adds support for the RTDM LPB (LocalPlusBus) FIFO driver
for the MPC5200. It will be used for DMA support in an RTDM FPGA
device driver. This rt-fpga.c driver is a custom device driver,
but might be useful for other developers as well. Thats why I
included it here too.

The lpb-fifo driver is ported from the Linux kernel lpb-fifo
driver:

arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c

With all locking etc changed from Linux call to Xenomai/RTDM
calls.

Signed-off-by: Stefan Roese <[email protected]>
---
 ksrc/drivers/Config.in                        |   1 +
 ksrc/drivers/Kconfig                          |   1 +
 ksrc/drivers/Makefile                         |   6 +-
 ksrc/drivers/mpc5200_dma/Config.in            |  10 +
 ksrc/drivers/mpc5200_dma/Kconfig              |  10 +
 ksrc/drivers/mpc5200_dma/Makefile             |  31 ++
 ksrc/drivers/mpc5200_dma/rt-fpga.c            | 701 ++++++++++++++++++++++++++
 ksrc/drivers/mpc5200_dma/rt-fpga.h            |  35 ++
 ksrc/drivers/mpc5200_dma/rt_mpc52xx_lpbfifo.c | 569 +++++++++++++++++++++
 scripts/Modules.frag                          |   3 +-
 10 files changed, 1364 insertions(+), 3 deletions(-)
 create mode 100644 ksrc/drivers/mpc5200_dma/Config.in
 create mode 100644 ksrc/drivers/mpc5200_dma/Kconfig
 create mode 100644 ksrc/drivers/mpc5200_dma/Makefile
 create mode 100644 ksrc/drivers/mpc5200_dma/rt-fpga.c
 create mode 100644 ksrc/drivers/mpc5200_dma/rt-fpga.h
 create mode 100644 ksrc/drivers/mpc5200_dma/rt_mpc52xx_lpbfifo.c

diff --git a/ksrc/drivers/Config.in b/ksrc/drivers/Config.in
index 0e50c44..bab3107 100644
--- a/ksrc/drivers/Config.in
+++ b/ksrc/drivers/Config.in
@@ -12,5 +12,6 @@ comment 'Drivers'
        source drivers/xenomai/can/Config.in
        source drivers/xenomai/analogy/Config.in
        source drivers/xenomai/ipc/Config.in
+       source drivers/xenomai/mpc5200_dma/Config.in
 endmenu
 fi
diff --git a/ksrc/drivers/Kconfig b/ksrc/drivers/Kconfig
index 2a9bd8c..9ecc710 100644
--- a/ksrc/drivers/Kconfig
+++ b/ksrc/drivers/Kconfig
@@ -7,5 +7,6 @@ source "drivers/xenomai/testing/Kconfig"
 source "drivers/xenomai/can/Kconfig"
 source "drivers/xenomai/analogy/Kconfig"
 source "drivers/xenomai/ipc/Kconfig"
+source "drivers/xenomai/mpc5200_dma/Kconfig"
 
 endmenu
diff --git a/ksrc/drivers/Makefile b/ksrc/drivers/Makefile
index 4968d87..e50ea1e 100644
--- a/ksrc/drivers/Makefile
+++ b/ksrc/drivers/Makefile
@@ -2,13 +2,13 @@ ifneq ($(VERSION).$(PATCHLEVEL),2.4)
 
 # Makefile frag for Linux v2.6 and v3.x
 
-obj-$(CONFIG_XENOMAI) += serial/ testing/ can/ analogy/ ipc/
+obj-$(CONFIG_XENOMAI) += serial/ testing/ can/ analogy/ ipc/ mpc5200_dma/
 
 else
 
 # Makefile frag for Linux v2.4
 
-mod-subdirs := serial testing can analogy ipc
+mod-subdirs := serial testing can analogy ipc mpc5200_dma
 
 subdir-$(CONFIG_XENO_DRIVERS_16550A) += serial
 
@@ -20,6 +20,8 @@ subdir-$(CONFIG_XENO_DRIVERS_CAN) += can
 subdir-$(CONFIG_XENO_DRIVERS_ANALOGY) += analogy
 subdir-$(CONFIG_XENO_DRIVERS_MPIPE) += ipc
 
+subdir-$(CONFIG_XENO_DRIVERS_MPC5200_DMA) += mpc5200_dma
+
 include $(TOPDIR)/Rules.make
 
 endif
diff --git a/ksrc/drivers/mpc5200_dma/Config.in 
b/ksrc/drivers/mpc5200_dma/Config.in
new file mode 100644
index 0000000..367c4c6
--- /dev/null
+++ b/ksrc/drivers/mpc5200_dma/Config.in
@@ -0,0 +1,10 @@
+#
+# Xenomai configuration for Linux v2.4
+#
+
+mainmenu_option next_comment
+comment 'MPC5200 DMA drivers'
+
+dep_tristate 'MPC5200 DMA FPGA driver' CONFIG_XENO_DRIVERS_MPC5200_DMA 
$CONFIG_XENO_SKIN_RTDM
+
+endmenu
diff --git a/ksrc/drivers/mpc5200_dma/Kconfig b/ksrc/drivers/mpc5200_dma/Kconfig
new file mode 100644
index 0000000..bf977de
--- /dev/null
+++ b/ksrc/drivers/mpc5200_dma/Kconfig
@@ -0,0 +1,10 @@
+menu "MPC5200 DMA drivers"
+
+config XENO_DRIVERS_MPC5200_DMA
+       depends on XENO_SKIN_RTDM && PPC_MPC52xx && PPC_MPC5200_LPBFIFO
+       tristate "MPC5200 DMA based driver to receive data from an FPGA"
+       help
+       This is a driver to demonstrate the usage of the RTDM DMA
+       infrastructure on the MPC5200.
+
+endmenu
diff --git a/ksrc/drivers/mpc5200_dma/Makefile 
b/ksrc/drivers/mpc5200_dma/Makefile
new file mode 100644
index 0000000..151dc79
--- /dev/null
+++ b/ksrc/drivers/mpc5200_dma/Makefile
@@ -0,0 +1,31 @@
+ifneq ($(VERSION).$(PATCHLEVEL),2.4)
+
+# Makefile frag for Linux v2.6 and v3.x
+
+EXTRA_CFLAGS += -D__IN_XENOMAI__ -Iinclude/xenomai
+
+obj-$(CONFIG_XENO_DRIVERS_MPC5200_DMA) += xeno_rt-fpga.o 
xeno_rt_mpc52xx_lpbfifo.o
+
+xeno_rt-fpga-y := rt-fpga.o
+xeno_rt_mpc52xx_lpbfifo-y := rt_mpc52xx_lpbfifo.o
+
+else
+
+# Makefile frag for Linux v2.4
+
+O_TARGET := built-in.o
+
+obj-$(CONFIG_XENO_DRIVERS_MPC5200_DMA) += xeno_rt-fpga.o 
xeno_rt_mpc52xx_lpbfifo.o
+
+xeno_mpc5200_dma-objs := rt-fpga.o rt_mpc52xx_lpbfifo.o
+
+export-objs := $(xeno_mpc5200_dma-objs)
+
+EXTRA_CFLAGS += -D__IN_XENOMAI__ -I$(TOPDIR)/include/xenomai 
-I$(TOPDIR)/include/xenomai/compat
+
+include $(TOPDIR)/Rules.make
+
+xeno_mpc5200_dma.o: $(xeno_mpc5200_dma-objs)
+       $(LD) -r -o $@ $(xeno_mpc5200_dma-objs)
+
+endif
diff --git a/ksrc/drivers/mpc5200_dma/rt-fpga.c 
b/ksrc/drivers/mpc5200_dma/rt-fpga.c
new file mode 100644
index 0000000..eaed49d
--- /dev/null
+++ b/ksrc/drivers/mpc5200_dma/rt-fpga.c
@@ -0,0 +1,701 @@
+/*
+ *  Xenomai RTDM device driver for A3M071 FPGA with interrupt support
+ *
+ *  Copyright (C) 2012 Stefan Roese <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/dma-mapping.h>
+#include <asm/mpc52xx.h>
+
+/* Xenomai headers */
+#include <native/timer.h>
+#include <rtdm/rtdm_driver.h>
+
+#include "rt-fpga.h"
+
+extern int rt_mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_request *req);
+extern void rt_mpc52xx_lpbfifo_abort(struct mpc52xx_lpbfifo_request *req);
+
+/*
+ * Some switches for testing and/or evaluation. Should be removed
+ * from the final device driver version.
+ */
+
+/*
+ * NO_DMA:
+ *
+ * Define to enable memcpy() for blockdata transfer instead of DMA
+ * support. This is available only for test purposes. Should be disabled
+ * or even removed in the final driver version.
+ */
+#if 0
+#define NO_DMA
+#endif
+
+/*
+ * PRINT_READ_TIME:
+ *
+ * Print min and max times consumed by the blockdata transfer, either
+ * per DMA or per memcpy. Should be disbaled or even removed in the
+ * final driver version.
+ */
+#if 1
+#define PRINT_READ_TIME
+#endif
+
+/*
+ * DEBUG:
+ *
+ * Define to print debug informations. Should be disabled in the
+ * final driver of course.
+ */
+#if 0
+#define DEBUG
+#endif
+
+#ifdef DEBUG
+#define dbg(format, arg...)                                            \
+       printk("%s (%d): " format, __func__, __LINE__, ##arg)
+#else
+#define dbg(format, arg...)                                            \
+do {                                                                   \
+       if (0)                                                          \
+               printk("%s (%d): " format, __func__, __LINE__, ##arg);  \
+} while (0)
+#endif
+
+
+#define DRIVER_NAME            "rt_fpga"
+#define DRIVER_VERSION         "1.0"
+
+#define BLOCKDATA_MAX_SIZE     (16 << 10)
+
+#define DMA_TIMEOUT_NS (100 * 1000 * 1000)     /* 100ms timeout for DMA */
+
+/*
+ * Nothing used from the "master" address space right now in this
+ * driver
+ */
+struct fpga_master {
+};
+
+/*
+ * Only define the interrupt related variables here. They are
+ * needed for the ISR. All other offsets/variables are only
+ * used by the userspace application.
+ */
+struct fpga_slave {
+       u8 dummy[0x400];
+       u16 irq_enable;
+       u16 align0;
+       u16 irq_pending;
+       u16 align1;
+       u16 irq_ack;
+       u16 align2;
+};
+
+struct fpga_info {
+       struct fpga_master *fpga_master;
+       struct fpga_slave *fpga_slave;
+       u8 *blockdata_read_buffer;
+       phys_addr_t blockdata_read_buffer_phys;
+       u8 *blockdata_write_buffer;
+       u32 blockdata_read_offset;
+       u32 blockdata_read_count;
+       u32 blockdata_write_offset;
+       u32 blockdata_write_count;
+       u32 timeout;
+       int irq;
+       int chip_select;
+};
+
+struct fpga_ctx {
+       rtdm_lock_t lock;               /* lock to protect context struct */
+       rtdm_event_t read_event;        /* raised to unblock reader */
+       rtdm_event_t read_event_dma;
+       rtdm_task_t irq_task;
+       rtdm_event_t irq_event;
+       struct fpga_info *info;
+       rtdm_irq_t rtdm_irq;
+       u32 irq_status;
+       u32 blockdata_ready;
+       int cycles_missed;
+       int reading;
+
+       /* for sclpc fifo bestcomm */
+       struct mpc52xx_lpbfifo_request req;
+       int dmareq_queued;
+       int dma_running;
+};
+
+static void fpga_dma_done_callback(struct mpc52xx_lpbfifo_request *req)
+{
+       struct fpga_ctx *fpga_ctx = req->priv;
+
+       rtdm_event_signal(&fpga_ctx->read_event_dma);
+}
+
+#ifdef PRINT_READ_TIME
+static int time_min, time_max;
+#endif
+
+static void fpga_irq_task(void *arg)
+{
+       struct fpga_ctx *fpga_ctx = arg;
+       int ret;
+#ifdef PRINT_READ_TIME
+       RTIME old_time;
+       int dt;
+#endif
+
+       while (1) {
+               ret = rtdm_event_wait(&fpga_ctx->irq_event);
+               if (ret < 0) {
+                       printk("Error: irq_task waiting on event returned %d\n",
+                              ret);
+               }
+
+               /*
+                * Task continues - this means that the FPGA interrupt
+                * has been received and new data is ready in the FPGA
+                * for transfer from FPGA to MPC5200 RAM.
+                */
+
+#ifdef PRINT_READ_TIME
+               old_time = rt_timer_tsc();
+#endif
+
+#ifdef NO_DMA
+               /*
+                * Read blockdata into buffer via memcpy
+                */
+               memcpy(fpga_ctx->info->blockdata_read_buffer,
+                      (void *)fpga_ctx->info->fpga_slave +
+                      fpga_ctx->info->blockdata_read_offset,
+                      fpga_ctx->info->blockdata_read_count);
+#else
+               /*
+                * Submit the DMA request the the LPB-FIFO RT driver. This
+                * will use the DMA based read (MPC5200 BestComm) to free
+                * the CPU from this time-consuming local bus accesses.
+                * The DMA parameters (request struct) is already filled in
+                * the FPGA_CONFIG ioctl.
+                */
+               ret = rt_mpc52xx_lpbfifo_submit(&fpga_ctx->req);
+               if (ret < 0) {
+                       printk("Error: rt_mpc52xx_lpbfifo_submit returned 
%d\n", ret);
+                       return;
+               }
+
+               /*
+                * Wait for DMA to complete here
+                */
+               ret = rtdm_event_timedwait(&fpga_ctx->read_event_dma,
+                                          DMA_TIMEOUT_NS, NULL);
+               if (ret) {
+                       printk("Error: DMA timed out (ret=%d)!\n", ret);
+                       return;
+               }
+#endif
+
+#ifdef PRINT_READ_TIME
+               dt = rt_timer_tsc() - old_time;
+               if (dt < time_min)
+                       time_min = dt;
+               if (dt > time_max)
+                       time_max = dt;
+#endif
+
+               /*
+                * Mark data as ready in buffer
+                */
+               fpga_ctx->blockdata_ready = 1;
+
+               /*
+                * Send signal to read function - unblock potential reading
+                * process
+                */
+               rtdm_event_signal(&fpga_ctx->read_event);
+       }
+}
+
+static int fpga_irq_handler(rtdm_irq_t *irq_context)
+{
+       struct fpga_ctx *fpga_ctx;
+       u32 irq_status;
+
+       fpga_ctx = rtdm_irq_get_arg(irq_context, struct fpga_ctx);
+
+       rtdm_lock_get(&fpga_ctx->lock);
+
+       irq_status = __raw_readw(&fpga_ctx->info->fpga_slave->irq_pending);
+       dbg("irq_status=%08x\n", irq_status);
+       __raw_writew(irq_status, &fpga_ctx->info->fpga_slave->irq_ack);
+
+       /*
+        * Check if blockdata has already been read be application
+        */
+       if (fpga_ctx->blockdata_ready)
+               fpga_ctx->cycles_missed++;
+
+       /* Let the irq-task continue with the real data transfer */
+       rtdm_event_signal(&fpga_ctx->irq_event);
+
+       rtdm_lock_put(&fpga_ctx->lock);
+
+       return RTDM_IRQ_HANDLED;
+}
+
+static void fpga_cleanup_ctx(struct fpga_ctx *fpga_ctx)
+{
+       rtdm_task_destroy(&fpga_ctx->irq_task);
+       rtdm_event_destroy(&fpga_ctx->read_event);
+       rtdm_event_destroy(&fpga_ctx->read_event_dma);
+       rtdm_event_destroy(&fpga_ctx->irq_event);
+}
+
+static int fpga_open(struct rtdm_dev_context *context,
+                    rtdm_user_info_t *user_info, int oflags)
+{
+       struct fpga_ctx *fpga_ctx;
+       int ret = 0;
+
+       fpga_ctx = (struct fpga_ctx *)context->dev_private;
+       fpga_ctx->info = (struct fpga_info *)context->device->device_data;
+
+       rtdm_lock_init(&fpga_ctx->lock);
+       rtdm_event_init(&fpga_ctx->read_event, 0);
+       rtdm_event_init(&fpga_ctx->read_event_dma, 0);
+       rtdm_event_init(&fpga_ctx->irq_event, 0);
+       fpga_ctx->cycles_missed = 0;
+       fpga_ctx->blockdata_ready = 0;
+       fpga_ctx->reading = 0;
+
+#ifdef PRINT_READ_TIME
+       time_min = 1000000;
+       time_max = 0;
+#endif
+
+       ret = rtdm_task_init(&fpga_ctx->irq_task, "fpga_irq_task",
+                            fpga_irq_task, fpga_ctx,
+                            RTDM_TASK_HIGHEST_PRIORITY, 0);
+       if (ret) {
+               fpga_cleanup_ctx(fpga_ctx);
+               return ret;
+       }
+
+       ret = rtdm_irq_request(&fpga_ctx->rtdm_irq, fpga_ctx->info->irq,
+                              fpga_irq_handler, 0, DRIVER_NAME, fpga_ctx);
+       if (ret)
+               fpga_cleanup_ctx(fpga_ctx);
+
+       return ret;
+}
+
+static int fpga_close(struct rtdm_dev_context *context,
+                     rtdm_user_info_t *user_info)
+{
+       struct fpga_ctx *fpga_ctx;
+
+       fpga_ctx = (struct fpga_ctx *)context->dev_private;
+
+#ifdef PRINT_READ_TIME
+       printk("read_time_min=%lld read_time_max=%lld [ns]\n",
+              rt_timer_tsc2ns(time_min), rt_timer_tsc2ns(time_max));
+#endif
+
+       rtdm_irq_free(&fpga_ctx->rtdm_irq);
+       fpga_cleanup_ctx(fpga_ctx);
+
+       return 0;
+}
+
+static int fpga_ioctl(struct rtdm_dev_context *context,
+                     rtdm_user_info_t *user_info,
+                     unsigned int request, void *arg)
+{
+       struct fpga_ctx *fpga_ctx;
+       struct fpga_rw_data fpga_data_buf;
+       struct fpga_rw_data *fpga_data;
+       int struct_size = sizeof(struct fpga_rw_data);
+       int err = 0;
+
+       fpga_ctx = (struct fpga_ctx *)context->dev_private;
+       fpga_data = (struct fpga_rw_data *)arg;
+
+       switch (request) {
+       case FPGA_READ_WORD:
+               /*
+                * Read one 16bit word from FPGA
+                */
+               if (user_info) {
+                       err = rtdm_safe_copy_from_user(user_info,
+                                                      &fpga_data_buf,
+                                                      arg, struct_size);
+                       if (err)
+                               return err;
+
+                       fpga_data = &fpga_data_buf;
+               }
+               fpga_data->value = __raw_readw((void 
*)fpga_ctx->info->fpga_slave +
+                                              fpga_data->offset);
+
+               if (user_info)
+                       err = rtdm_safe_copy_to_user(user_info, arg,
+                                                    &fpga_data_buf,
+                                                    struct_size);
+               else
+                       ((struct fpga_rw_data *)arg)->value = fpga_data->value;
+
+               break;
+
+       case FPGA_WRITE_WORD:
+               /*
+                * Write one 16bit word to FPGA
+                */
+               if (user_info) {
+                       err = rtdm_safe_copy_from_user(user_info,
+                                                      &fpga_data_buf,
+                                                      arg, struct_size);
+                       if (err)
+                               return err;
+
+                       fpga_data = &fpga_data_buf;
+               }
+               __raw_writew(fpga_data->value,
+                            (void *)fpga_ctx->info->fpga_slave +
+                            fpga_data->offset);
+
+               break;
+
+       case FPGA_CONFIG:
+               if (user_info) {
+                       err = rtdm_safe_copy_from_user(user_info,
+                                                      &fpga_data_buf,
+                                                      arg, struct_size);
+                       if (err)
+                               return err;
+
+                       fpga_data = &fpga_data_buf;
+               }
+
+               fpga_ctx->info->blockdata_read_offset = fpga_data->offset;
+               fpga_ctx->info->blockdata_read_count = fpga_data->count;
+               fpga_ctx->info->blockdata_write_offset = 
fpga_data->write_offset;
+               fpga_ctx->info->blockdata_write_count = fpga_data->write_count;
+
+               /*
+                * Setup request for upcoming DMA transfer
+                */
+               fpga_ctx->req.cs = fpga_ctx->info->chip_select;
+               fpga_ctx->req.offset = fpga_ctx->info->blockdata_read_offset;
+               fpga_ctx->req.data = fpga_ctx->info->blockdata_read_buffer;
+               fpga_ctx->req.data_phys = 
fpga_ctx->info->blockdata_read_buffer_phys;
+               fpga_ctx->req.size = fpga_ctx->info->blockdata_read_count;
+               fpga_ctx->req.pos = 0;
+               fpga_ctx->req.flags = MPC52XX_LPBFIFO_FLAG_READ;
+               fpga_ctx->req.callback = fpga_dma_done_callback;
+               fpga_ctx->req.priv = fpga_ctx;
+
+               break;
+
+       case FPGA_SET_TIMEOUT:
+               if (fpga_ctx->reading)
+                       return -EBUSY;
+
+               if (user_info) {
+                       err = rtdm_safe_copy_from_user(user_info,
+                                                      &fpga_data_buf,
+                                                      arg, struct_size);
+                       if (err)
+                               return err;
+
+                       fpga_data = &fpga_data_buf;
+               }
+
+               /*
+                * FPGA_SET_TIMEOUT is used to start the blockdata
+                * read operation. Upon this call we mark the reading
+                * in process, so that other processes will return
+                * with -EBUSY.
+                */
+               fpga_ctx->info->timeout =
+                       (nanosecs_rel_t)fpga_data->timeout * 1000;
+               fpga_ctx->reading = 1;
+               break;
+
+       case FPGA_GET_CYCLES_MISSED:
+               if (user_info) {
+                       fpga_data_buf.cycles_missed = fpga_ctx->cycles_missed;
+                       err = rtdm_safe_copy_to_user(user_info, arg,
+                                                    &fpga_data_buf,
+                                                    struct_size);
+               } else
+                       ((struct fpga_rw_data *)arg)->cycles_missed =
+                               fpga_ctx->cycles_missed;
+
+               /*
+                * Reset cycles_missed and reading in process flags
+                */
+               fpga_ctx->cycles_missed = 0;
+               fpga_ctx->reading = 0;
+               break;
+       }
+
+       return 0;
+}
+
+static ssize_t fpga_read(struct rtdm_dev_context *context,
+                        rtdm_user_info_t *user_info, void *buf, size_t nbyte)
+{
+       rtdm_lockctx_t lock_ctx;
+       struct fpga_ctx *fpga_ctx;
+       ssize_t ret;
+       int count;
+
+       if (nbyte == 0)
+               return 0;
+
+       if (user_info && !rtdm_rw_user_ok(user_info, buf, nbyte))
+               return -EFAULT;
+
+       fpga_ctx = (struct fpga_ctx *)context->dev_private;
+
+       /*
+        * Block until the blockdata is completely read into
+        * the buffer (done in interrupt)
+        */
+       ret = rtdm_event_timedwait(&fpga_ctx->read_event,
+                                  fpga_ctx->info->timeout, NULL);
+
+       /*
+        * Mark blockdata as read
+        */
+       fpga_ctx->blockdata_ready = 0;
+
+       if (ret)
+               return ret;
+
+       rtdm_lock_get_irqsave(&fpga_ctx->lock, lock_ctx);
+
+       /*
+        * Transfer data to user
+        */
+       count = min(nbyte, fpga_ctx->info->blockdata_read_count);
+       if (user_info) {
+               if (rtdm_copy_to_user(user_info, buf,
+                                     fpga_ctx->info->blockdata_read_buffer,
+                                     count) != 0) {
+                       return -EFAULT;
+               }
+       } else
+               memcpy(buf, fpga_ctx->info->blockdata_read_buffer, count);
+
+       rtdm_lock_put_irqrestore(&fpga_ctx->lock, lock_ctx);
+
+       return count;
+}
+
+static ssize_t fpga_write(struct rtdm_dev_context *context,
+                         rtdm_user_info_t  *user_info,
+                         const void *buf, size_t nbyte)
+{
+       rtdm_lockctx_t lock_ctx;
+       struct fpga_ctx *fpga_ctx;
+       int count;
+
+       if (nbyte == 0)
+               return 0;
+
+       if (user_info && !rtdm_read_user_ok(user_info, buf, nbyte))
+               return -EFAULT;
+
+       fpga_ctx = (struct fpga_ctx *)context->dev_private;
+
+       rtdm_lock_get_irqsave(&fpga_ctx->lock, lock_ctx);
+
+       /*
+        * Copy blockdata from userspace
+        */
+       if (user_info) {
+               if (rtdm_copy_from_user(user_info,
+                                       fpga_ctx->info->blockdata_write_buffer,
+                                       buf, nbyte) != 0) {
+                       rtdm_lock_put_irqrestore(&fpga_ctx->lock, lock_ctx);
+                       return -EFAULT;
+               }
+       } else {
+               memcpy(fpga_ctx->info->blockdata_write_buffer, buf, nbyte);
+       }
+
+       /*
+        * Transfer data to FPGA
+        */
+       count = min(nbyte, fpga_ctx->info->blockdata_write_count);
+       memcpy((void *)fpga_ctx->info->fpga_slave +
+              fpga_ctx->info->blockdata_write_offset,
+              fpga_ctx->info->blockdata_write_buffer, count);
+
+       rtdm_lock_put_irqrestore(&fpga_ctx->lock, lock_ctx);
+
+       return count;
+}
+
+static const struct rtdm_device __devinitdata device_tmpl = {
+       .struct_version         = RTDM_DEVICE_STRUCT_VER,
+
+       .device_flags           = RTDM_NAMED_DEVICE | RTDM_EXCLUSIVE,
+       .context_size           = sizeof(struct fpga_ctx),
+       .device_name            = "",
+
+       .open_nrt               = fpga_open,
+
+       .ops = {
+               .close_nrt      = fpga_close,
+
+               .ioctl_rt       = fpga_ioctl,
+               .ioctl_nrt      = fpga_ioctl,
+
+               .read_rt        = fpga_read,
+
+               .write_rt       = fpga_write,
+       },
+
+       .device_class           = RTDM_CLASS_TESTING,
+       .driver_name            = "a3m071-fpga",
+       .driver_version         = RTDM_DRIVER_VER(1, 0, 0),
+       .peripheral_name        = "A3M071 FPGA",
+       .provider_name          = "Stefan Roese",
+};
+
+static int __devinit fpga_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct device *dev = &pdev->dev;
+       struct fpga_info *fpga_info;
+       struct rtdm_device *rtdm_dev;
+       const unsigned int *prop;
+       u32 val;
+       int ret = 0;
+
+       fpga_info = devm_kzalloc(dev, sizeof(struct fpga_info), GFP_KERNEL);
+
+       fpga_info->fpga_slave = of_iomap(np, 0);
+       if (!fpga_info->fpga_slave) {
+               dev_err(dev, "ioremap failed\n");
+               return -ENOMEM;
+       }
+
+       fpga_info->fpga_master = of_iomap(np, 1);
+       if (!fpga_info->fpga_master) {
+               dev_err(dev, "ioremap failed\n");
+               return -ENOMEM;
+       }
+
+       fpga_info->irq = platform_get_irq(pdev, 0);
+       if (fpga_info->irq < 0) {
+               dev_err(dev, "invalid FPGA irq\n");
+               return -ENODEV;
+       }
+
+       fpga_info->blockdata_read_buffer =
+               dmam_alloc_coherent(dev, BLOCKDATA_MAX_SIZE,
+                                   &fpga_info->blockdata_read_buffer_phys,
+                                   GFP_KERNEL);
+       if (!fpga_info->blockdata_read_buffer) {
+               dev_err(dev, "Could allocate blockdata read buffer\n");
+               return -ENOMEM;
+       }
+
+       fpga_info->blockdata_write_buffer = devm_kzalloc(dev, 
BLOCKDATA_MAX_SIZE,
+                                                        GFP_KERNEL);
+       if (!fpga_info->blockdata_write_buffer) {
+               dev_err(dev, "Could allocate blockdata write buffer\n");
+               return -ENOMEM;
+       }
+
+       rtdm_dev = devm_kzalloc(dev, sizeof(struct rtdm_device), GFP_KERNEL);
+       if (!rtdm_dev) {
+               dev_err(dev, "Could allocate device context\n");
+               return -ENOMEM;
+       }
+
+       /* find CS # */
+       prop = of_get_property(np, "reg", &val);
+       if (!prop || !*prop || *prop > 7) {
+               dev_err(&pdev->dev, "invalid reg property\n");
+               return -EINVAL;
+       }
+       fpga_info->chip_select = *prop;
+
+       memcpy(rtdm_dev, &device_tmpl, sizeof(struct rtdm_device));
+       snprintf(rtdm_dev->device_name, RTDM_MAX_DEVNAME_LEN, "fpga-a3m071");
+       rtdm_dev->proc_name = rtdm_dev->device_name;
+       rtdm_dev->device_data = fpga_info;
+
+       ret = rtdm_dev_register(rtdm_dev);
+       if (ret)
+               return ret;
+
+       dev_set_drvdata(dev, rtdm_dev);
+
+       dev_info(dev, "%s driver registration successful\n", DRIVER_NAME);
+
+       return 0;
+}
+
+static int fpga_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rtdm_device *rtdm_dev = dev_get_drvdata(dev);
+       struct fpga_info *fpga_info = rtdm_dev->device_data;
+
+        iounmap(fpga_info->fpga_master);
+        iounmap(fpga_info->fpga_slave);
+
+       rtdm_dev_unregister(rtdm_dev, 1000);
+
+       dev_info(dev, "%s driver unregistered\n", DRIVER_NAME);
+
+       return 0;
+}
+
+static const struct of_device_id fpga_id_table[] = {
+       { .compatible = "anonymous,a3m071-fpga" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, fpga_id_table);
+
+static struct platform_driver fpga_driver = {
+       .probe = fpga_probe,
+       .remove = fpga_remove,
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = DRIVER_NAME,
+               .of_match_table = of_match_ptr(fpga_id_table),
+       },
+};
+
+module_platform_driver(fpga_driver);
+
+MODULE_AUTHOR("Stefan Roese <[email protected]>");
+MODULE_DESCRIPTION("Xenomai A3M071 FPGA device driver");
+MODULE_LICENSE("GPL");
diff --git a/ksrc/drivers/mpc5200_dma/rt-fpga.h 
b/ksrc/drivers/mpc5200_dma/rt-fpga.h
new file mode 100644
index 0000000..f25cf2e
--- /dev/null
+++ b/ksrc/drivers/mpc5200_dma/rt-fpga.h
@@ -0,0 +1,35 @@
+/*
+ *  Xenomai RTDM device driver for A3M071 FPGA with interrupt support
+ *
+ *  Copyright (C) 2012 Stefan Roese <[email protected]>
+ */
+
+#ifndef _RT_FPGA_H
+#define _RT_FPGA_H
+
+/*
+ * IOCTL's for this FPGA driver
+ */
+#define RTIOC_TYPE_TESTING             RTDM_CLASS_TESTING
+#define FPGA_CONFIG    \
+       _IOW(RTIOC_TYPE_TESTING, 0x00, struct fpga_rw_data)
+#define FPGA_READ_WORD \
+       _IOR(RTIOC_TYPE_TESTING, 0x01, struct fpga_rw_data)
+#define FPGA_WRITE_WORD        \
+       _IOW(RTIOC_TYPE_TESTING, 0x02, struct fpga_rw_data)
+#define FPGA_SET_TIMEOUT       \
+       _IOW(RTIOC_TYPE_TESTING, 0x03, struct fpga_rw_data)
+#define FPGA_GET_CYCLES_MISSED \
+       _IOR(RTIOC_TYPE_TESTING, 0x04, struct fpga_rw_data)
+
+struct fpga_rw_data {
+       unsigned int offset;
+       unsigned short value;
+       unsigned int count;
+       unsigned int write_offset;
+       unsigned int write_count;
+       unsigned int timeout;
+       int cycles_missed;
+};
+
+#endif
diff --git a/ksrc/drivers/mpc5200_dma/rt_mpc52xx_lpbfifo.c 
b/ksrc/drivers/mpc5200_dma/rt_mpc52xx_lpbfifo.c
new file mode 100644
index 0000000..8305f43
--- /dev/null
+++ b/ksrc/drivers/mpc5200_dma/rt_mpc52xx_lpbfifo.c
@@ -0,0 +1,569 @@
+/*
+ * LocalPlus Bus FIFO driver for the Freescale MPC52xx.
+ *
+ * Copyright (C) 2009 Secret Lab Technologies Ltd.
+ *
+ * Ported to Xenomai (RTDM): Stefan Roese <[email protected]>
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/mpc52xx.h>
+#include <asm/time.h>
+
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/bestcomm_priv.h>
+#include <sysdev/bestcomm/gen_bd.h>
+
+/* Xenomai headers */
+#include <rtdm/rtdm_driver.h>
+
+MODULE_AUTHOR("Grant Likely <[email protected]>");
+MODULE_DESCRIPTION("RT MPC5200 LocalPlus FIFO device driver");
+MODULE_LICENSE("GPL");
+
+#define LPBFIFO_REG_PACKET_SIZE                (0x00)
+#define LPBFIFO_REG_START_ADDRESS      (0x04)
+#define LPBFIFO_REG_CONTROL            (0x08)
+#define LPBFIFO_REG_ENABLE             (0x0C)
+#define LPBFIFO_REG_BYTES_DONE_STATUS  (0x14)
+#define LPBFIFO_REG_FIFO_DATA          (0x40)
+#define LPBFIFO_REG_FIFO_STATUS                (0x44)
+#define LPBFIFO_REG_FIFO_CONTROL       (0x48)
+#define LPBFIFO_REG_FIFO_ALARM         (0x4C)
+
+struct mpc52xx_lpbfifo {
+       struct device *dev;
+       phys_addr_t regs_phys;
+       void __iomem *regs;
+       int irq;
+       rtdm_lock_t lock;
+
+       struct bcom_task *bcom_tx_task;
+       struct bcom_task *bcom_rx_task;
+       struct bcom_task *bcom_cur_task;
+
+       /* Current state data */
+       struct mpc52xx_lpbfifo_request *req;
+       int dma_irqs_enabled;
+
+       rtdm_irq_t rtdm_irq_bcom_rx;
+       rtdm_irq_t rtdm_irq_bcom_tx;
+       rtdm_irq_t rtdm_irq_lpbfifo;
+};
+
+/* The MPC5200 has only one fifo, so only need one instance structure */
+static struct mpc52xx_lpbfifo lpbfifo;
+
+/**
+ * mpc52xx_lpbfifo_kick - Trigger the next block of data to be transferred
+ */
+static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req)
+{
+       size_t transfer_size = req->size - req->pos;
+       struct bcom_bd *bd;
+       void __iomem *reg;
+       u32 *data;
+       int i;
+       int bit_fields;
+       int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
+       int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;
+       int poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;
+
+       /* Set and clear the reset bits; is good practice in User Manual */
+       out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
+
+       /* set master enable bit */
+       out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000001);
+       if (!dma) {
+               /* While the FIFO can be setup for transfer sizes as large as
+                * 16M-1, the FIFO itself is only 512 bytes deep and it does
+                * not generate interrupts for FIFO full events (only transfer
+                * complete will raise an IRQ).  Therefore when not using
+                * Bestcomm to drive the FIFO it needs to either be polled, or
+                * transfers need to constrained to the size of the fifo.
+                *
+                * This driver restricts the size of the transfer
+                */
+               if (transfer_size > 512)
+                       transfer_size = 512;
+
+               /* Load the FIFO with data */
+               if (write) {
+                       reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA;
+                       data = req->data + req->pos;
+                       for (i = 0; i < transfer_size; i += 4)
+                               out_be32(reg, *data++);
+               }
+
+               /* Unmask both error and completion irqs */
+               out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000301);
+       } else {
+               /* Choose the correct direction
+                *
+                * Configure the watermarks so DMA will always complete 
correctly.
+                * It may be worth experimenting with the ALARM value to see if
+                * there is a performance impacit.  However, if it is wrong 
there
+                * is a risk of DMA not transferring the last chunk of data
+                */
+               if (write) {
+                       out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1e4);
+                       out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 7);
+                       lpbfifo.bcom_cur_task = lpbfifo.bcom_tx_task;
+               } else {
+                       out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1ff);
+                       out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 0);
+                       lpbfifo.bcom_cur_task = lpbfifo.bcom_rx_task;
+
+                       if (poll_dma) {
+                               if (lpbfifo.dma_irqs_enabled) {
+                                       
disable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task));
+                                       lpbfifo.dma_irqs_enabled = 0;
+                               }
+                       } else {
+                               if (!lpbfifo.dma_irqs_enabled) {
+                                       
enable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task));
+                                       lpbfifo.dma_irqs_enabled = 1;
+                               }
+                       }
+               }
+
+               bd = bcom_prepare_next_buffer(lpbfifo.bcom_cur_task);
+               bd->status = transfer_size;
+               if (!write) {
+                       /*
+                        * In the DMA read case, the DMA doesn't complete,
+                        * possibly due to incorrect watermarks in the ALARM
+                        * and CONTROL regs. For now instead of trying to
+                        * determine the right watermarks that will make this
+                        * work, just increase the number of bytes the FIFO is
+                        * expecting.
+                        *
+                        * When submitting another operation, the FIFO will get
+                        * reset, so the condition of the FIFO waiting for a
+                        * non-existent 4 bytes will get cleared.
+                        */
+                       transfer_size += 4; /* BLECH! */
+               }
+               bd->data[0] = req->data_phys + req->pos;
+               bcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL);
+
+               /* error irq & master enabled bit */
+               bit_fields = 0x00000201;
+
+               /* Unmask irqs */
+               if (write && (!poll_dma))
+                       bit_fields |= 0x00000100; /* completion irq too */
+               out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, bit_fields);
+       }
+
+       /* Set transfer size, width, chip select and READ mode */
+       out_be32(lpbfifo.regs + LPBFIFO_REG_START_ADDRESS,
+                req->offset + req->pos);
+       out_be32(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, transfer_size);
+
+       bit_fields = req->cs << 24 | 0x000008;
+       if (!write)
+               bit_fields |= 0x010000; /* read mode */
+       out_be32(lpbfifo.regs + LPBFIFO_REG_CONTROL, bit_fields);
+
+       /* Kick it off */
+       out_8(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, 0x01);
+       if (dma)
+               bcom_enable(lpbfifo.bcom_cur_task);
+}
+
+/**
+ * mpc52xx_lpbfifo_irq - IRQ handler for LPB FIFO
+ *
+ * On transmit, the dma completion irq triggers before the fifo completion
+ * triggers.  Handle the dma completion here instead of the LPB FIFO Bestcomm
+ * task completion irq because everything is not really done until the LPB FIFO
+ * completion irq triggers.
+ *
+ * In other words:
+ * For DMA, on receive, the "Fat Lady" is the bestcom completion irq. on
+ * transmit, the fifo completion irq is the "Fat Lady". The opera (or in this
+ * case the DMA/FIFO operation) is not finished until the "Fat Lady" sings.
+ *
+ * Reasons for entering this routine:
+ * 1) PIO mode rx and tx completion irq
+ * 2) DMA interrupt mode tx completion irq
+ * 3) DMA polled mode tx
+ *
+ * Exit conditions:
+ * 1) Transfer aborted
+ * 2) FIFO complete without DMA; more data to do
+ * 3) FIFO complete without DMA; all data transferred
+ * 4) FIFO complete using DMA
+ *
+ * Condition 1 can occur regardless of whether or not DMA is used.
+ * It requires executing the callback to report the error and exiting
+ * immediately.
+ *
+ * Condition 2 requires programming the FIFO with the next block of data
+ *
+ * Condition 3 requires executing the callback to report completion
+ *
+ * Condition 4 means the same as 3, except that we also retrieve the bcom
+ * buffer so DMA doesn't get clogged up.
+ *
+ * To make things trickier, the spinlock must be dropped before
+ * executing the callback, otherwise we could end up with a deadlock
+ * or nested spinlock condition.  The out path is non-trivial, so
+ * extra fiddling is done to make sure all paths lead to the same
+ * outbound code.
+ */
+static int mpc52xx_lpbfifo_irq(rtdm_irq_t *irq_context)
+{
+       struct mpc52xx_lpbfifo_request *req;
+       u32 status = in_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS);
+       void __iomem *reg;
+       u32 *data;
+       int count, i;
+       int do_callback = 0;
+       u32 ts;
+       int dma, write, poll_dma;
+       rtdm_lockctx_t lock_ctx;
+
+       rtdm_lock_get_irqsave(&lpbfifo.lock, lock_ctx);
+       ts = get_tbl();
+
+       req = lpbfifo.req;
+       if (!req) {
+               rtdm_lock_put_irqrestore(&lpbfifo.lock, lock_ctx);
+               pr_err("bogus LPBFIFO IRQ\n");
+               return RTDM_IRQ_HANDLED;
+       }
+
+       dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
+       write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;
+       poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;
+
+       if (dma && !write) {
+               rtdm_lock_put_irqrestore(&lpbfifo.lock, lock_ctx);
+               pr_err("bogus LPBFIFO IRQ (dma and not writting)\n");
+               return RTDM_IRQ_HANDLED;
+       }
+
+       if ((status & 0x01) == 0) {
+               goto out;
+       }
+
+       /* check abort bit */
+       if (status & 0x10) {
+               out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
+               do_callback = 1;
+               goto out;
+       }
+
+       /* Read result from hardware */
+       count = in_be32(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS);
+       count &= 0x00ffffff;
+
+       if (!dma && !write) {
+               /* copy the data out of the FIFO */
+               reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA;
+               data = req->data + req->pos;
+               for (i = 0; i < count; i += 4)
+                       *data++ = in_be32(reg);
+       }
+
+       /* Update transfer position and count */
+       req->pos += count;
+
+       /* Decide what to do next */
+       if (req->size - req->pos)
+               mpc52xx_lpbfifo_kick(req); /* more work to do */
+       else
+               do_callback = 1;
+
+ out:
+       /* Clear the IRQ */
+       out_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS, 0x01);
+
+       if (dma && (status & 0x11)) {
+               /*
+                * Count the DMA as complete only when the FIFO completion
+                * status or abort bits are set.
+                *
+                * (status & 0x01) should always be the case except sometimes
+                * when using polled DMA.
+                *
+                * (status & 0x10) {transfer aborted}: This case needs more
+                * testing.
+                */
+               bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL);
+       }
+       req->last_byte = ((u8 *)req->data)[req->size - 1];
+
+       /* When the do_callback flag is set; it means the transfer is finished
+        * so set the FIFO as idle */
+       if (do_callback)
+               lpbfifo.req = NULL;
+
+       if (irq_context != NULL) /* don't increment on polled case */
+               req->irq_count++;
+
+       req->irq_ticks += get_tbl() - ts;
+       rtdm_lock_put_irqrestore(&lpbfifo.lock, lock_ctx);
+
+       /* Spinlock is released; it is now safe to call the callback */
+       if (do_callback && req->callback)
+               req->callback(req);
+
+       return RTDM_IRQ_HANDLED;
+}
+
+/**
+ * mpc52xx_lpbfifo_bcom_irq - IRQ handler for LPB FIFO Bestcomm task
+ *
+ * Only used when receiving data.
+ */
+static int mpc52xx_lpbfifo_bcom_irq(rtdm_irq_t *irq_context)
+{
+       struct mpc52xx_lpbfifo_request *req;
+       u32 status;
+       u32 ts;
+       rtdm_lockctx_t lock_ctx;
+
+       rtdm_lock_get_irqsave(&lpbfifo.lock, lock_ctx);
+       ts = get_tbl();
+
+       req = lpbfifo.req;
+       if (!req || (req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA)) {
+               rtdm_lock_put_irqrestore(&lpbfifo.lock, lock_ctx);
+               return RTDM_IRQ_HANDLED;
+       }
+
+       if (irq_context != NULL) /* don't increment on polled case */
+               req->irq_count++;
+
+       if (!bcom_buffer_done(lpbfifo.bcom_cur_task)) {
+               rtdm_lock_put_irqrestore(&lpbfifo.lock, lock_ctx);
+
+               req->buffer_not_done_cnt++;
+               if ((req->buffer_not_done_cnt % 1000) == 0)
+                       pr_err("transfer stalled\n");
+
+               return RTDM_IRQ_HANDLED;
+       }
+
+       bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL);
+
+       req->last_byte = ((u8 *)req->data)[req->size - 1];
+
+       req->pos = status & 0x00ffffff;
+
+       /* Mark the FIFO as idle */
+       lpbfifo.req = NULL;
+
+       /* Release the lock before calling out to the callback. */
+       req->irq_ticks += get_tbl() - ts;
+       rtdm_lock_put_irqrestore(&lpbfifo.lock, lock_ctx);
+
+       if (req->callback)
+               req->callback(req);
+
+       return RTDM_IRQ_HANDLED;
+}
+
+/**
+ * rt_mpc52xx_lpbfifo_bcom_poll - Poll for DMA completion
+ */
+void rt_mpc52xx_lpbfifo_poll(void)
+{
+       struct mpc52xx_lpbfifo_request *req = lpbfifo.req;
+       int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);
+       int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;
+
+       /*
+        * For more information, see comments on the "Fat Lady"
+        */
+       if (dma && write)
+               mpc52xx_lpbfifo_irq(NULL);
+       else
+               mpc52xx_lpbfifo_bcom_irq(NULL);
+}
+EXPORT_SYMBOL(rt_mpc52xx_lpbfifo_poll);
+
+/**
+ * rt_mpc52xx_lpbfifo_submit - Submit an LPB FIFO transfer request.
+ * @req: Pointer to request structure
+ */
+int rt_mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_request *req)
+{
+       rtdm_lockctx_t lock_ctx;
+
+       if (!lpbfifo.regs)
+               return -ENODEV;
+
+       rtdm_lock_get_irqsave(&lpbfifo.lock, lock_ctx);
+
+       /* If the req pointer is already set, then a transfer is in progress */
+       if (lpbfifo.req) {
+               rtdm_lock_put_irqrestore(&lpbfifo.lock, lock_ctx);
+               return -EBUSY;
+       }
+
+       /* Setup the transfer */
+       lpbfifo.req = req;
+       req->irq_count = 0;
+       req->irq_ticks = 0;
+       req->buffer_not_done_cnt = 0;
+       req->pos = 0;
+
+       mpc52xx_lpbfifo_kick(req);
+       rtdm_lock_put_irqrestore(&lpbfifo.lock, lock_ctx);
+       return 0;
+}
+EXPORT_SYMBOL(rt_mpc52xx_lpbfifo_submit);
+
+void rt_mpc52xx_lpbfifo_abort(struct mpc52xx_lpbfifo_request *req)
+{
+       rtdm_lockctx_t lock_ctx;
+
+       rtdm_lock_get_irqsave(&lpbfifo.lock, lock_ctx);
+       if (lpbfifo.req == req) {
+               /* Put it into reset and clear the state */
+               bcom_gen_bd_rx_reset(lpbfifo.bcom_rx_task);
+               bcom_gen_bd_tx_reset(lpbfifo.bcom_tx_task);
+               out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
+               lpbfifo.req = NULL;
+       }
+       rtdm_lock_put_irqrestore(&lpbfifo.lock, lock_ctx);
+}
+EXPORT_SYMBOL(rt_mpc52xx_lpbfifo_abort);
+
+static int __devinit mpc52xx_lpbfifo_probe(struct platform_device *op)
+{
+       struct resource res;
+       int rc = -ENOMEM;
+
+       if (lpbfifo.dev != NULL)
+               return -ENOSPC;
+
+       lpbfifo.irq = irq_of_parse_and_map(op->dev.of_node, 0);
+       if (!lpbfifo.irq)
+               return -ENODEV;
+
+       if (of_address_to_resource(op->dev.of_node, 0, &res))
+               return -ENODEV;
+       lpbfifo.regs_phys = res.start;
+       lpbfifo.regs = of_iomap(op->dev.of_node, 0);
+       if (!lpbfifo.regs)
+               return -ENOMEM;
+
+       rtdm_lock_init(&lpbfifo.lock);
+
+       /* Put FIFO into reset */
+       out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
+
+       /* Register the interrupt handler */
+       rc = rtdm_irq_request(&lpbfifo.rtdm_irq_lpbfifo, lpbfifo.irq,
+                             mpc52xx_lpbfifo_irq, 0,
+                             "mpc52xx-lpbfifo", &lpbfifo);
+       if (rc)
+               goto err_irq;
+
+       /* Request the Bestcomm receive (fifo --> memory) task and IRQ */
+       lpbfifo.bcom_rx_task =
+               bcom_gen_bd_rx_init(2, res.start + LPBFIFO_REG_FIFO_DATA,
+                                   BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC,
+                                   16*1024*1024);
+       if (!lpbfifo.bcom_rx_task)
+               goto err_bcom_rx;
+
+       rc = rtdm_irq_request(&lpbfifo.rtdm_irq_bcom_rx,
+                             bcom_get_task_irq(lpbfifo.bcom_rx_task),
+                             mpc52xx_lpbfifo_bcom_irq, 0,
+                             "mpc52xx-lpbfifo-rx", &lpbfifo);
+       if (rc)
+               goto err_bcom_rx_irq;
+
+       lpbfifo.dma_irqs_enabled = 1;
+
+       /* Request the Bestcomm transmit (memory --> fifo) task and IRQ */
+       lpbfifo.bcom_tx_task =
+               bcom_gen_bd_tx_init(2, res.start + LPBFIFO_REG_FIFO_DATA,
+                                   BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC);
+       if (!lpbfifo.bcom_tx_task)
+               goto err_bcom_tx;
+
+       lpbfifo.dev = &op->dev;
+       return 0;
+
+ err_bcom_tx:
+       rtdm_irq_free(&lpbfifo.rtdm_irq_bcom_rx);
+ err_bcom_rx_irq:
+       bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task);
+ err_bcom_rx:
+ err_irq:
+       iounmap(lpbfifo.regs);
+       lpbfifo.regs = NULL;
+
+       dev_err(&op->dev, "mpc52xx_lpbfifo_probe() failed\n");
+       return -ENODEV;
+}
+
+
+static int __devexit mpc52xx_lpbfifo_remove(struct platform_device *op)
+{
+       if (lpbfifo.dev != &op->dev)
+               return 0;
+
+       /* Put FIFO in reset */
+       out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);
+
+       /* Release the bestcomm transmit task */
+       rtdm_irq_free(&lpbfifo.rtdm_irq_bcom_tx);
+       bcom_gen_bd_tx_release(lpbfifo.bcom_tx_task);
+
+       /* Release the bestcomm receive task */
+       rtdm_irq_free(&lpbfifo.rtdm_irq_bcom_rx);
+       bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task);
+
+       rtdm_irq_free(&lpbfifo.rtdm_irq_lpbfifo);
+       iounmap(lpbfifo.regs);
+       lpbfifo.regs = NULL;
+       lpbfifo.dev = NULL;
+
+       return 0;
+}
+
+static struct of_device_id mpc52xx_lpbfifo_match[] __devinitconst = {
+       { .compatible = "fsl,rt-mpc5200-lpbfifo", },
+       {},
+};
+
+static struct platform_driver mpc52xx_lpbfifo_driver = {
+       .driver = {
+               .name = "rt-mpc52xx-lpbfifo",
+               .owner = THIS_MODULE,
+               .of_match_table = mpc52xx_lpbfifo_match,
+       },
+       .probe = mpc52xx_lpbfifo_probe,
+       .remove = __devexit_p(mpc52xx_lpbfifo_remove),
+};
+
+/***********************************************************************
+ * Module init/exit
+ */
+static int __init mpc52xx_lpbfifo_init(void)
+{
+       return platform_driver_register(&mpc52xx_lpbfifo_driver);
+}
+module_init(mpc52xx_lpbfifo_init);
+
+static void __exit mpc52xx_lpbfifo_exit(void)
+{
+       platform_driver_unregister(&mpc52xx_lpbfifo_driver);
+}
+module_exit(mpc52xx_lpbfifo_exit);
diff --git a/scripts/Modules.frag b/scripts/Modules.frag
index d5e2ca4..24ad34a 100644
--- a/scripts/Modules.frag
+++ b/scripts/Modules.frag
@@ -21,4 +21,5 @@ DRIVERS-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_PEAK_PCI) += 
drivers/xenomai/can/sja10
 DRIVERS-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_PEAK_DNG) += 
drivers/xenomai/can/sja1000/xeno_can_peak_dng.o
 DRIVERS-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_IXXAT_PCI) += 
drivers/xenomai/can/sja1000/xeno_can_ixxat_pci.o
 DRIVERS-$(CONFIG_XENO_DRIVERS_CAN_SJA1000_EMS_PCI) += 
drivers/xenomai/can/sja1000/xeno_can_ems_pci.o
-
+DRIVERS-$(CONFIG_XENO_DRIVERS_MPC5200_DMA) += 
drivers/xenomai/mpc5200_dma/rt-fpga.o
+DRIVERS-$(CONFIG_XENO_DRIVERS_MPC5200_DMA) += 
drivers/xenomai/mpc5200_dma/rt_mpc52xx_lpbfifo.o
-- 
1.8.0


_______________________________________________
Xenomai mailing list
[email protected]
http://www.xenomai.org/mailman/listinfo/xenomai

Reply via email to