use broken-out patches for the coldfire to make it easier to follow differences again...
[openwrt/svn-archive/archive.git] / target / linux / coldfire / patches / 019-m5445x_spi.patch
diff --git a/target/linux/coldfire/patches/019-m5445x_spi.patch b/target/linux/coldfire/patches/019-m5445x_spi.patch
new file mode 100644 (file)
index 0000000..49b83e0
--- /dev/null
@@ -0,0 +1,3345 @@
+From bc755a3b8859e7307a8b10f39ca4cb6401c51987 Mon Sep 17 00:00:00 2001
+From: Kurt Mahan <kmahan@freescale.com>
+Date: Tue, 27 Nov 2007 14:39:37 -0700
+Subject: [PATCH] Add M5445x SPI support.
+
+LTIBName: m5445x-spi
+Signed-off-by: Kurt Mahan <kmahan@freescale.com>
+---
+ arch/m68k/configs/m54455evb_defconfig |   24 +-
+ drivers/spi/Kconfig                   |   36 +
+ drivers/spi/Makefile                  |    4 +
+ drivers/spi/coldfire_edma.c           |  358 ++++++++
+ drivers/spi/spi-m5445x.c              |  156 ++++
+ drivers/spi/spi_coldfire.c            | 1552 +++++++++++++++++++++++++++++++++
+ drivers/spi/ssi_audio.c               |  906 +++++++++++++++++++
+ include/asm-m68k/coldfire_edma.h      |  101 ++-
+ include/linux/spi/mcfqspi.h           |   80 ++
+ 9 files changed, 3196 insertions(+), 21 deletions(-)
+ create mode 100644 drivers/spi/coldfire_edma.c
+ create mode 100644 drivers/spi/spi-m5445x.c
+ create mode 100644 drivers/spi/spi_coldfire.c
+ create mode 100644 drivers/spi/ssi_audio.c
+ create mode 100644 include/linux/spi/mcfqspi.h
+
+--- a/arch/m68k/configs/m54455evb_defconfig
++++ b/arch/m68k/configs/m54455evb_defconfig
+@@ -321,6 +321,8 @@ CONFIG_MTD_PHYSMAP_BANKWIDTH=1
+ #
+ # Self-contained MTD device drivers
+ #
++# CONFIG_MTD_DATAFLASH is not set
++# CONFIG_MTD_M25P80 is not set
+ # CONFIG_MTD_SLRAM is not set
+ # CONFIG_MTD_PHRAM is not set
+ # CONFIG_MTD_MTDRAM is not set
+@@ -497,8 +499,26 @@ CONFIG_UNIX98_PTYS=y
+ #
+ # SPI support
+ #
+-# CONFIG_SPI is not set
+-# CONFIG_SPI_MASTER is not set
++CONFIG_SPI=y
++# CONFIG_SPI_DEBUG is not set
++CONFIG_COLDFIRE_EDMA=y
++CONFIG_SPI_MASTER=y
++
++#
++# SPI Master Controller Drivers
++#
++# CONFIG_SPI_BITBANG is not set
++CONFIG_SPI_COLDFIRE=y
++CONFIG_SPI_COLDFIRE_DSPI_EDMA=y
++
++#
++# SPI Protocol Masters
++#
++# CONFIG_SPI_AT25 is not set
++# CONFIG_SPI_SPIDEV is not set
++# CONFIG_SPI_TLE62X0 is not set
++CONFIG_SPI_COLDFIRE_SSI_AUDIO=y
++# CONFIG_SSIAUDIO_USE_EDMA is not set
+ # CONFIG_W1 is not set
+ # CONFIG_POWER_SUPPLY is not set
+ # CONFIG_HWMON is not set
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -35,6 +35,15 @@ config SPI_DEBUG
+         Say "yes" to enable debug messaging (like dev_dbg and pr_debug),
+         sysfs, and debugfs support in SPI controller and protocol drivers.
++config COLDFIRE_EDMA
++      tristate "Coldfire eDMA"
++      depends on COLDFIRE && EXPERIMENTAL
++      help
++          Support for Coldfire eDMA controller. Required for example
++          by SSI audio device driver.
++
++
++
+ #
+ # MASTER side ... talking to discrete SPI slave chips including microcontrollers
+ #
+@@ -113,6 +122,21 @@ config SPI_GPIO
+         If unsure, say N.
++config SPI_COLDFIRE
++      tristate "Coldfire QSPI/DSPI SPI Master"
++      depends on SPI_MASTER && COLDFIRE && EXPERIMENTAL
++      help
++        SPI driver for Freescale Coldfire QSPI module in master mode.
++        Tested with the 5282 processor, but should also work with other
++        Coldfire variants.
++
++config SPI_COLDFIRE_DSPI_EDMA   
++      boolean "Coldfire DSPI master driver uses eDMA"
++      depends on SPI_MASTER && COLDFIRE && SPI_COLDFIRE && EXPERIMENTAL && COLDFIRE_EDMA
++      default n
++      help
++        Say "yes" if you want DSPI master driver to use eDMA for transfers.
++
+ config SPI_IMX
+       tristate "Freescale iMX SPI controller"
+       depends on SPI_MASTER && ARCH_IMX && EXPERIMENTAL
+@@ -255,6 +279,18 @@ config SPI_TLE62X0
+ #
+ # Add new SPI protocol masters in alphabetical order above this line
+ #
++config SPI_COLDFIRE_SSI_AUDIO
++      tristate "Coldfire SSI AUDIO"
++      depends on SPI_MASTER && SPI_COLDFIRE && EXPERIMENTAL
++      help
++        SSI audio device driver
++
++config SSIAUDIO_USE_EDMA
++      boolean "Coldfire DSPI master driver uses eDMA"
++      default y
++      depends on EXPERIMENTAL && COLDFIRE_EDMA && SPI_COLDFIRE_SSI_AUDIO
++      help
++        Say "yes" if you want SSI audio driver to use eDMA for SSI transfers.
+ # (slave support would go here)
+--- a/drivers/spi/Makefile
++++ b/drivers/spi/Makefile
+@@ -6,6 +6,8 @@ ifeq ($(CONFIG_SPI_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+ endif
++obj-$(CONFIG_COLDFIRE_EDMA)           += coldfire_edma.o
++
+ # small core, mostly translating board-specific
+ # config declarations into driver model code
+ obj-$(CONFIG_SPI_MASTER)              += spi.o
+@@ -16,6 +18,7 @@ obj-$(CONFIG_SPI_BFIN)                       += spi_bfin5xx.
+ obj-$(CONFIG_SPI_BITBANG)             += spi_bitbang.o
+ obj-$(CONFIG_SPI_AU1550)              += au1550_spi.o
+ obj-$(CONFIG_SPI_BUTTERFLY)           += spi_butterfly.o
++obj-$(CONFIG_SPI_COLDFIRE)            += spi_coldfire.o spi-m5445x.o
+ obj-$(CONFIG_SPI_GPIO)                        += spi_gpio.o
+ obj-$(CONFIG_SPI_IMX)                 += spi_imx.o
+ obj-$(CONFIG_SPI_LM70_LLP)            += spi_lm70llp.o
+@@ -35,6 +38,7 @@ obj-$(CONFIG_SPI_SH_SCI)             += spi_sh_sci.
+ obj-$(CONFIG_SPI_AT25)                += at25.o
+ obj-$(CONFIG_SPI_SPIDEV)      += spidev.o
+ obj-$(CONFIG_SPI_TLE62X0)     += tle62x0.o
++obj-$(CONFIG_SPI_COLDFIRE_SSI_AUDIO)  += ssi_audio.o
+ #     ... add above this line ...
+ # SPI slave controller drivers (upstream link)
+--- /dev/null
++++ b/drivers/spi/coldfire_edma.c
+@@ -0,0 +1,358 @@
++/*
++ *
++ * coldfire_edma.c - eDMA driver for Coldfire MCF5445x
++ *
++ * Yaroslav Vinogradov yaroslav.vinogradov@freescale.com
++ *
++ * Copyright Freescale Semiconductor, Inc. 2007
++ *
++ * 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/init.h>
++#include <linux/module.h>
++#include <asm/virtconvert.h>
++#include <asm/coldfire.h>
++#include <linux/fs.h>
++#include <linux/cdev.h>
++#include <linux/seq_file.h>
++#include <linux/proc_fs.h>
++#include <asm/mcf5445x_edma.h>
++#include <asm/mcf5445x_intc.h>
++#include <asm/coldfire_edma.h>
++
++
++/* callback handler data for each TCD */
++struct edma_isr_record {
++      edma_irq_handler irq_handler;       /* interrupt handler */
++      edma_error_handler error_handler;       /* error interrupt handler */
++      void* dev;                                                      /* device used for the channel */
++      int  allocated;                                         /* busy flag */
++      spinlock_t *lock;                                       /* spin lock (if needs to be locked in interrupt) */
++      const char* device_id;                          /* device id string, used in proc file system */
++};
++
++/* device structure */
++struct coldfire_edma_dev {
++      struct cdev cdev;                       /* character device */
++      struct edma_isr_record dma_interrupt_handlers[EDMA_CHANNELS]; /* channel handlers */
++};
++
++/* allocated major device number */
++static int coldfire_dma_major;
++/* device driver structure */
++static struct coldfire_edma_dev* devp = NULL;
++
++/* device driver file operations */
++struct file_operations coldfire_edma_fops = {
++      .owner = THIS_MODULE,
++};
++
++/* eDMA channel interrupt handler */
++static int dmaisr(int irq, void *dev_id)
++{
++      int channel = irq - EDMA_INT_CONTROLLER_BASE - EDMA_INT_CHANNEL_BASE;
++      int result = IRQ_HANDLED;
++
++      if (devp!=NULL && devp->dma_interrupt_handlers[channel].lock) {
++              spin_lock(devp->dma_interrupt_handlers[channel].lock);
++      }
++
++      if (devp!=NULL && devp->dma_interrupt_handlers[channel].irq_handler) {
++              result = devp->dma_interrupt_handlers[channel].irq_handler(channel,
++                                                      devp->dma_interrupt_handlers[channel].dev);
++      } else {
++              confirm_edma_interrupt_handled(channel);
++              printk(EDMA_DRIVER_NAME ": No handler for DMA channel %d\n", channel);
++      }
++
++      if (devp!=NULL && devp->dma_interrupt_handlers[channel].lock) {
++              spin_unlock(devp->dma_interrupt_handlers[channel].lock);
++      }
++
++      return result;
++}
++
++/* eDMA error interrupt handler */
++static int dma_error_isr(int irq, void* dev_id)
++{
++      u16 err;
++      int i;
++
++      err = MCF_EDMA_ERR;
++      for (i=0;i<EDMA_CHANNELS;i++) {
++              if (err & (1<<i)) {
++                      if (devp!=NULL && devp->dma_interrupt_handlers[i].error_handler) {
++                              devp->dma_interrupt_handlers[i].error_handler(i, devp->dma_interrupt_handlers[i].dev);
++                      } else {
++                              printk(KERN_WARNING EDMA_DRIVER_NAME ": DMA error on channel %d\n", i);
++                      }
++              }
++      }
++
++      MCF_EDMA_CERR = MCF_EDMA_CERR_CAER;
++      return IRQ_HANDLED;
++}
++
++/* sets channel parameters */
++void set_edma_params(int channel, u32 source, u32 dest,
++                                       u32 attr, u32 soff, u32 nbytes, u32 slast,
++                                       u32 citer, u32 biter, u32 doff, u32 dlast_sga,
++                                       int major_int, int disable_req)
++{
++
++      if (channel<0 || channel>EDMA_CHANNELS)
++              return;
++
++      MCF_EDMA_TCD_SADDR(channel) = source;
++      MCF_EDMA_TCD_DADDR(channel) = dest;
++      MCF_EDMA_TCD_ATTR(channel) = attr;
++      MCF_EDMA_TCD_SOFF(channel) = MCF_EDMA_TCD_SOFF_SOFF(soff);
++      MCF_EDMA_TCD_NBYTES(channel) = MCF_EDMA_TCD_NBYTES_NBYTES(nbytes);
++      MCF_EDMA_TCD_SLAST(channel) = MCF_EDMA_TCD_SLAST_SLAST(slast);
++      MCF_EDMA_TCD_CITER(channel) = MCF_EDMA_TCD_CITER_CITER(citer);
++      MCF_EDMA_TCD_BITER(channel)=MCF_EDMA_TCD_BITER_BITER(biter);
++      MCF_EDMA_TCD_DOFF(channel) = MCF_EDMA_TCD_DOFF_DOFF(doff);
++      MCF_EDMA_TCD_DLAST_SGA(channel) = MCF_EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga);
++      /* interrupt at the end of major loop */
++      if (major_int) {
++              MCF_EDMA_TCD_CSR(channel) |= MCF_EDMA_TCD_CSR_INT_MAJOR;
++      } else {
++              MCF_EDMA_TCD_CSR(channel) &= ~MCF_EDMA_TCD_CSR_INT_MAJOR;
++      }
++      /* disable request at the end of major loop of transfer or not*/
++      if (disable_req) {
++              MCF_EDMA_TCD_CSR(channel) |= MCF_EDMA_TCD_CSR_D_REQ;
++      } else {
++              MCF_EDMA_TCD_CSR(channel) &= ~MCF_EDMA_TCD_CSR_D_REQ;
++      }
++
++}
++EXPORT_SYMBOL(set_edma_params);
++
++/* init eDMA controller */
++void init_edma(void)
++{
++      MCF_EDMA_CR = 0;
++}
++EXPORT_SYMBOL(init_edma);
++
++/* request eDMA channel */
++int request_edma_channel(int channel,
++                                              edma_irq_handler handler,
++                                              edma_error_handler error_handler,
++                                              void* dev,
++                                              spinlock_t *lock,
++                                              const char* device_id )
++{
++      if (devp!=NULL && channel>=0 && channel<=EDMA_CHANNELS) {
++              if (devp->dma_interrupt_handlers[channel].allocated) {
++                      return -EBUSY;
++              }
++              devp->dma_interrupt_handlers[channel].allocated = 1;
++              devp->dma_interrupt_handlers[channel].irq_handler = handler;
++              devp->dma_interrupt_handlers[channel].error_handler = error_handler;
++              devp->dma_interrupt_handlers[channel].dev = dev;
++              devp->dma_interrupt_handlers[channel].lock = lock;
++              devp->dma_interrupt_handlers[channel].device_id = device_id;
++              return 0;
++      }
++      return -EINVAL;
++}
++EXPORT_SYMBOL(request_edma_channel);
++
++/* free eDMA channel */
++int free_edma_channel(int channel, void* dev)
++{
++      if (devp!=NULL && channel>=0 && channel<=EDMA_CHANNELS) {
++              if (devp->dma_interrupt_handlers[channel].allocated) {
++                      if (devp->dma_interrupt_handlers[channel].dev != dev) {
++                              return -EBUSY;
++                      }
++                      devp->dma_interrupt_handlers[channel].allocated = 0;
++                      devp->dma_interrupt_handlers[channel].dev = NULL;
++                      devp->dma_interrupt_handlers[channel].irq_handler = NULL;
++                      devp->dma_interrupt_handlers[channel].error_handler = NULL;
++                      devp->dma_interrupt_handlers[channel].lock = NULL;
++              }
++              return 0;
++      }
++      return -EINVAL;
++}
++EXPORT_SYMBOL(free_edma_channel);
++
++/* clean-up device driver allocated resources */
++static void coldfire_edma_cleanup(void)
++{
++      dev_t devno;
++      int i;
++
++      /* free interrupts/memory */
++      if (devp) {
++              for (i=0;i<EDMA_CHANNELS;i++)
++              {
++                      MCF_INTC0_SIMR = EDMA_INT_CHANNEL_BASE+i;
++                      free_irq(EDMA_INT_CHANNEL_BASE+EDMA_INT_CONTROLLER_BASE+i,      devp);
++              }
++              MCF_INTC0_SIMR = EDMA_INT_CHANNEL_BASE+EDMA_CHANNELS;
++              free_irq(EDMA_INT_CHANNEL_BASE+EDMA_INT_CONTROLLER_BASE+EDMA_CHANNELS, devp);
++              cdev_del(&devp->cdev);
++              kfree(devp);
++      }
++
++      /* unregister character device */
++      devno = MKDEV(coldfire_dma_major, 0);
++      unregister_chrdev_region(devno, 1);
++}
++
++#ifdef CONFIG_PROC_FS
++/* proc file system support */
++
++#define FREE_CHANNEL "free"
++#define DEVICE_UNKNOWN "device unknown"
++
++static int proc_edma_show(struct seq_file *m, void *v)
++{
++      int i;
++
++      if (devp==NULL) return 0;
++
++      for (i = 0 ; i < EDMA_CHANNELS ; i++) {
++              if (devp->dma_interrupt_handlers[i].allocated) {
++                      if (devp->dma_interrupt_handlers[i].device_id)
++                      seq_printf(m, "%2d: %s\n", i, devp->dma_interrupt_handlers[i].device_id);
++                      else
++                              seq_printf(m, "%2d: %s\n", i, DEVICE_UNKNOWN);
++              } else {
++                      seq_printf(m, "%2d: %s\n", i, FREE_CHANNEL);
++              }
++      }
++      return 0;
++}
++
++static int proc_edma_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, proc_edma_show, NULL);
++}
++
++static const struct file_operations proc_edma_operations = {
++      .open           = proc_edma_open,
++      .read           = seq_read,
++      .llseek         = seq_lseek,
++      .release        = single_release,
++};
++
++static int __init proc_edma_init(void)
++{
++      struct proc_dir_entry *e;
++
++      e = create_proc_entry("edma", 0, NULL);
++      if (e)
++              e->proc_fops = &proc_edma_operations;
++
++      return 0;
++}
++
++#endif
++
++/* initializes device driver */
++static int __init coldfire_edma_init(void)
++{
++      dev_t dev;
++      int result;
++      int i;
++
++      /* allocate free major number */
++      result = alloc_chrdev_region(&dev, DMA_DEV_MINOR, 1, EDMA_DRIVER_NAME);
++      if (result<0) {
++              printk(KERN_WARNING EDMA_DRIVER_NAME": can't get major %d\n", result);
++              return result;
++      }
++      coldfire_dma_major = MAJOR(dev);
++
++      /* allocate device driver structure */
++      devp = kmalloc(sizeof(struct coldfire_edma_dev), GFP_KERNEL);
++      if (!devp) {
++              result = -ENOMEM;
++              goto fail;
++      }
++
++      /* init handlers (no handlers for beggining) */
++      for (i=0;i<EDMA_CHANNELS;i++) {
++              devp->dma_interrupt_handlers[i].irq_handler = NULL;
++              devp->dma_interrupt_handlers[i].error_handler = NULL;
++              devp->dma_interrupt_handlers[i].dev = NULL;
++              devp->dma_interrupt_handlers[i].allocated = 0;
++              devp->dma_interrupt_handlers[i].lock = NULL;
++              devp->dma_interrupt_handlers[i].device_id = NULL;
++      }
++
++    /* register char device */
++      cdev_init(&devp->cdev, &coldfire_edma_fops);
++      devp->cdev.owner = THIS_MODULE;
++      devp->cdev.ops = &coldfire_edma_fops;
++      result = cdev_add(&devp->cdev, dev, 1);
++      if (result) {
++              printk(KERN_NOTICE EDMA_DRIVER_NAME": Error %d adding coldfire-dma device\n", result);
++              result = -ENODEV;
++              goto fail;
++      }
++
++      /* request/enable irq for each eDMA channel */
++      for (i=0;i<EDMA_CHANNELS;i++)
++      {
++              result = request_irq(EDMA_INT_CHANNEL_BASE+EDMA_INT_CONTROLLER_BASE+i,
++                      dmaisr, SA_INTERRUPT, EDMA_DRIVER_NAME, devp);
++              if (result) {
++                      printk(KERN_WARNING EDMA_DRIVER_NAME": Cannot request irq %d\n",
++                              EDMA_INT_CHANNEL_BASE+EDMA_INT_CONTROLLER_BASE+i);
++                      result = -EBUSY;
++                      goto fail;
++              }
++
++              MCF_INTC0_ICR(EDMA_INT_CHANNEL_BASE+i) = EDMA_IRQ_LEVEL;
++              MCF_INTC0_CIMR = EDMA_INT_CHANNEL_BASE+i;
++
++      }
++
++    /* request error interrupt */
++      result = request_irq(EDMA_INT_CHANNEL_BASE + EDMA_INT_CONTROLLER_BASE + EDMA_CHANNELS,
++                              dma_error_isr, SA_INTERRUPT, EDMA_DRIVER_NAME, devp);
++      if (result) {
++              printk(KERN_WARNING EDMA_DRIVER_NAME": Cannot request irq %d\n",
++                              EDMA_INT_CHANNEL_BASE+EDMA_INT_CONTROLLER_BASE+EDMA_CHANNELS);
++              result = -EBUSY;
++              goto fail;
++      }
++
++      /* enable error interrupt in interrupt controller */
++      MCF_INTC0_ICR(EDMA_INT_CHANNEL_BASE+EDMA_CHANNELS) = EDMA_IRQ_LEVEL;
++      MCF_INTC0_CIMR = EDMA_INT_CHANNEL_BASE+EDMA_CHANNELS;
++
++#ifdef CONFIG_PROC_FS
++      proc_edma_init();
++#endif
++
++      printk(EDMA_DRIVER_NAME ": initialized successfully\n");
++
++      return 0;
++fail:
++      coldfire_edma_cleanup();
++      return result;
++
++}
++
++static void __exit coldfire_edma_exit(void)
++{
++      coldfire_edma_cleanup();
++}
++
++module_init(coldfire_edma_init);
++module_exit(coldfire_edma_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Yaroslav Vinogradov, Freescale Inc.");
++MODULE_DESCRIPTION("eDMA library for Coldfire 5445x");
+--- /dev/null
++++ b/drivers/spi/spi-m5445x.c
+@@ -0,0 +1,156 @@
++/***************************************************************************/
++/*
++ *    linux/arch/m68k/coldfire/spi-m5445x.c
++ *
++ *    Sub-architcture dependant initialization code for the Freescale
++ *    5445x SPI module
++ *
++ *    Yaroslav Vinogradov yaroslav.vinogradov@freescale.com
++ *    Copyright Freescale Semiconductor, Inc 2007
++ *
++ *    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/kernel.h>
++#include <linux/sched.h>
++#include <linux/param.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/spi/spi.h>
++
++#include <asm/dma.h>
++#include <asm/traps.h>
++#include <asm/machdep.h>
++#include <asm/coldfire.h>
++#include <asm/mcfsim.h>
++#include <asm/mcfqspi.h>
++#include <asm/mcf5445x_gpio.h>
++
++#define SPI_NUM_CHIPSELECTS   0x10
++#define SPI_PAR_VAL  (0 | MCF_GPIO_PAR_DSPI_PCS5_PCS5 | MCF_GPIO_PAR_DSPI_PCS2_PCS2 \
++              | MCF_GPIO_PAR_DSPI_PCS1_PCS1 | MCF_GPIO_PAR_DSPI_PCS0_PCS0 | MCF_GPIO_PAR_DSPI_SIN_SIN \
++              | MCF_GPIO_PAR_DSPI_SOUT_SOUT | MCF_GPIO_PAR_DSPI_SCK_SCK)
++
++#define MCF5445x_DSPI_IRQ_SOURCE      (31)
++#define MCF5445x_DSPI_IRQ_VECTOR      (64 + MCF5445x_DSPI_IRQ_SOURCE)
++
++#define MCF5445x_DSPI_PAR     (0xFC0A4063)
++#define MCF5445x_DSPI_MCR     (0xFC05C000)
++#define MCF5445x_INTC0_ICR    (0xFC048040)
++#define MCF5445x_INTC0_IMRL   (0xFC04800C)
++
++
++#define M5445x_AUDIO_IRQ_SOURCE 49
++#define M5445x_AUDIO_IRQ_VECTOR (128+M5445x_AUDIO_IRQ_SOURCE)
++#define M5445x_AUDIO_IRQ_LEVEL        4
++
++void coldfire_qspi_cs_control(u8 cs, u8 command)
++{
++}
++
++#if defined(CONFIG_SPI_COLDFIRE_SSI_AUDIO)
++static struct coldfire_spi_chip ssi_audio_chip_info = {
++      .mode = SPI_MODE_0,
++      .bits_per_word = 16,
++      .del_cs_to_clk = 16,
++      .del_after_trans = 16,
++      .void_write_data = 0
++};
++
++#endif
++
++static struct spi_board_info spi_board_info[] = {
++
++#if defined(CONFIG_SPI_COLDFIRE_SSI_AUDIO)
++      {
++              .modalias = "ssi_audio",
++              .max_speed_hz = 300000,
++              .bus_num = 1,
++              .chip_select = 5,
++              .irq = M5445x_AUDIO_IRQ_VECTOR,
++              .platform_data = NULL,
++              .controller_data = &ssi_audio_chip_info
++      }
++#endif
++
++};
++
++static struct coldfire_spi_master coldfire_master_info = {
++      .bus_num = 1,
++      .num_chipselect = SPI_NUM_CHIPSELECTS,
++      .irq_source = MCF5445x_DSPI_IRQ_SOURCE,
++      .irq_vector = MCF5445x_DSPI_IRQ_VECTOR,
++      .irq_mask = (0x01 << MCF5445x_DSPI_IRQ_SOURCE),
++      .irq_lp = 0x2,  /* Level */
++      .par_val = SPI_PAR_VAL,
++//    .par_val16 = SPI_PAR_VAL,
++      .cs_control = coldfire_qspi_cs_control,
++};
++
++static struct resource coldfire_spi_resources[] = {
++      [0] = {
++              .name = "qspi-par",
++              .start = MCF5445x_DSPI_PAR,
++              .end = MCF5445x_DSPI_PAR,
++              .flags = IORESOURCE_MEM
++      },
++
++      [1] = {
++              .name = "qspi-module",
++              .start = MCF5445x_DSPI_MCR,
++              .end = MCF5445x_DSPI_MCR + 0xB8,
++              .flags = IORESOURCE_MEM
++      },
++
++      [2] = {
++              .name = "qspi-int-level",
++              .start = MCF5445x_INTC0_ICR + MCF5445x_DSPI_IRQ_SOURCE,
++              .end = MCF5445x_INTC0_ICR + MCF5445x_DSPI_IRQ_SOURCE,
++              .flags = IORESOURCE_MEM
++      },
++
++      [3] = {
++              .name = "qspi-int-mask",
++              .start = MCF5445x_INTC0_IMRL,
++              .end = MCF5445x_INTC0_IMRL,
++              .flags = IORESOURCE_MEM
++      }
++};
++
++static struct platform_device coldfire_spi = {
++      .name = "spi_coldfire", //"coldfire-qspi",
++      .id = -1,
++      .resource = coldfire_spi_resources,
++      .num_resources = ARRAY_SIZE(coldfire_spi_resources),
++      .dev = {
++              .platform_data = &coldfire_master_info,
++      }
++};
++
++static int __init spi_dev_init(void)
++{
++      int retval = 0;
++      
++      retval = platform_device_register(&coldfire_spi);
++
++      if (retval < 0) {
++              printk(KERN_ERR "SPI-m5445x: platform_device_register failed with code=%d\n", retval);
++              goto out;
++      }
++
++      if (ARRAY_SIZE(spi_board_info))
++              retval = spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
++
++
++out:
++      return retval;
++}
++
++arch_initcall(spi_dev_init);
+--- /dev/null
++++ b/drivers/spi/spi_coldfire.c
+@@ -0,0 +1,1552 @@
++/****************************************************************************/
++
++/*
++ *    spi_coldfire.c - Master QSPI/DSPI controller for the ColdFire processors
++ *
++ *    (C) Copyright 2005, Intec Automation,
++ *                        Mike Lavender (mike@steroidmicros)
++ *
++ *    (C) Copyright 2007, Freescale Inc,
++ *                        Yaroslav Vinogradov (yaroslav.vinogradov@freescale.com)
++ *
++
++     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.
++
++     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.                     */
++/* ------------------------------------------------------------------------- */
++
++
++/****************************************************************************/
++
++/*
++ * Includes
++ */
++
++#include <linux/autoconf.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/spi/spi.h>
++#include <linux/workqueue.h>
++#include <linux/delay.h>
++
++#include <asm/delay.h>
++#include <asm/mcfsim.h>
++#include <asm/mcfqspi.h>
++#include <asm/coldfire.h>
++#include <asm/virtconvert.h>
++
++#if defined(CONFIG_M54455)
++      #define SPI_DSPI
++      #if defined(CONFIG_SPI_COLDFIRE_DSPI_EDMA)
++              #define SPI_DSPI_EDMA
++              #ifdef CONFIG_MMU
++                      #define SPI_USE_MMU
++              #endif
++      #endif
++#endif
++
++#ifdef SPI_DSPI
++#include <asm/mcf5445x_dspi.h>
++
++
++#endif
++
++#if defined(SPI_DSPI_EDMA)
++
++/* edma buffer size in transfer units (32bits) */
++#define EDMA_BUFFER_SIZE      (PAGE_SIZE/4)
++#define EDMA_BUFSIZE_KMALLOC (EDMA_BUFFER_SIZE*4)
++
++#define DSPI_DMA_RX_TCD       12
++#define DSPI_DMA_TX_TCD 13
++
++
++#include <asm/coldfire_edma.h>
++#include <asm/mcf5445x_edma.h>
++#endif
++
++
++MODULE_AUTHOR("Mike Lavender");
++MODULE_DESCRIPTION("ColdFire QSPI Contoller");
++MODULE_LICENSE("GPL");
++
++#define DRIVER_NAME "Coldfire QSPI/DSPI"
++
++/****************************************************************************/
++
++/*
++ * Local constants and macros
++ */
++
++#define QSPI_RAM_SIZE         0x10    /* 16 word table */
++
++#define QSPI_TRANSMIT_RAM     0x00
++#define QSPI_RECEIVE_RAM      0x10
++#define QSPI_COMMAND_RAM      0x20
++
++#define QSPI_COMMAND          0x7000  /* 15:   X = Continuous CS
++                                       * 14:   1 = Get BITSE from QMR[BITS]
++                                       * 13:   1 = Get DT    from QDLYR[DTL]
++                                       * 12:   1 = Get DSK   from QDLYR[QCD]
++                                       * 8-11: XXXX = next 4 bytes for CS
++                                       * 0-7:  0000 0000 Reserved
++                                       */
++
++#define QIR_WCEF                0x0008  /* write collison */
++#define QIR_ABRT                0x0004  /* abort */
++#define QIR_SPIF                0x0001  /* finished */
++
++#define QIR_WCEFE                     0x0800
++#define QIR_ABRTE                     0x0400
++#define QIR_SPIFE                     0x0100
++
++#define QIR_WCEFB                     0x8000
++#define QIR_ABRTB                     0x4000
++#define QIR_ABRTL                     0x1000
++
++#define QMR_BITS              0x3C00
++#define QMR_BITS_8              0x2000
++
++#define QCR_CONT                      0x8000
++
++#define QDLYR_SPE             0x8000
++
++#define QWR_ENDQP_MASK                0x0F00
++#define QWR_CSIV              0x1000  /* 1 = active low chip selects */
++
++
++#define START_STATE ((void*)0)
++#define RUNNING_STATE ((void*)1)
++#define DONE_STATE ((void*)2)
++#define ERROR_STATE ((void*)-1)
++
++#define QUEUE_RUNNING 0
++#define QUEUE_STOPPED 1
++
++/****************************************************************************/
++
++/*
++ * Local Data Structures
++ */
++
++struct transfer_state {
++      u32 index;
++      u32 len;
++      void *tx;
++      void *tx_end;
++      void *rx;
++      void *rx_end;
++      char flags;
++#define TRAN_STATE_RX_VOID       0x01
++#define TRAN_STATE_TX_VOID       0x02
++#define TRAN_STATE_WORD_ODD_NUM          0x04
++      u8 cs;
++      u16 void_write_data;
++      unsigned cs_change:1;
++};
++
++typedef struct {
++      unsigned master:1;
++      unsigned dohie:1;
++      unsigned bits:4;
++      unsigned cpol:1;
++      unsigned cpha:1;
++      unsigned baud:8;
++} QMR;
++
++typedef struct {
++      unsigned spe:1;
++      unsigned qcd:7;
++      unsigned dtl:8;
++} QDLYR;
++
++typedef struct {
++      unsigned halt:1;
++      unsigned wren:1;
++      unsigned wrto:1;
++      unsigned csiv:1;
++      unsigned endqp:4;
++      unsigned cptqp:4;
++      unsigned newqp:4;
++} QWR;
++
++
++typedef struct {
++      unsigned master:1;
++      unsigned cont_scke:1;
++      unsigned dconf:2;
++      unsigned frz:1;
++      unsigned mtfe:1;
++      unsigned pcsse:1;
++      unsigned rooe:1;
++      unsigned pcsis:8;
++      unsigned reserved15:1;
++      unsigned mdis:1;
++      unsigned dis_tx:1;
++      unsigned dis_rxf:1;
++      unsigned clr_tx:1;
++      unsigned clr_rxf:1;
++      unsigned smpl_pt:2;
++      unsigned reserved71:7;
++      unsigned halt:1;
++} DSPI_MCR;
++
++typedef struct {
++      unsigned dbr:1;
++      unsigned fmsz:4;
++      unsigned cpol:1;
++      unsigned cpha:1;
++      unsigned lsbfe:1;
++      unsigned pcssck:2;
++      unsigned pasc:2;
++      unsigned pdt:2;
++      unsigned pbr:2;
++      unsigned cssck:4;
++      unsigned asc:4;
++      unsigned dt:4;
++      unsigned br:4;
++} DSPI_CTAR;
++
++struct chip_data {
++#if defined(SPI_DSPI)
++      /* dspi data */
++      union {
++              u32 mcr_val;
++              DSPI_MCR mcr;
++      };
++      union {
++              u32 ctar_val;
++              DSPI_CTAR ctar;
++      };
++#else
++      union {
++              u16 qmr_val;
++              QMR qmr;
++      };
++      union {
++              u16 qdlyr_val;
++              QDLYR qdlyr;
++      };
++      union {
++              u16 qwr_val;
++              QWR qwr;
++      };
++#endif
++
++      u16 void_write_data;
++};
++
++
++struct driver_data {
++      /* Driver model hookup */
++      struct platform_device *pdev;
++
++      /* SPI framework hookup */
++      struct spi_master *master;
++
++      /* Driver message queue */
++      struct workqueue_struct *workqueue;
++      struct work_struct pump_messages;
++      spinlock_t lock;
++      struct list_head queue;
++      int busy;
++      int run;
++
++      /* Message Transfer pump */
++      struct tasklet_struct pump_transfers;
++
++      /* Current message transfer state info */
++      struct spi_message* cur_msg;
++      struct spi_transfer* cur_transfer;
++      struct chip_data *cur_chip;
++      size_t len;
++      void *tx;
++      void *tx_end;
++      void *rx;
++      void *rx_end;
++      char flags;
++#define TRAN_STATE_RX_VOID       0x01
++#define TRAN_STATE_TX_VOID       0x02
++#define TRAN_STATE_WORD_ODD_NUM          0x04
++      u8 cs;
++      u16 void_write_data;
++      unsigned cs_change:1;
++
++      u32 trans_cnt;
++      u32 wce_cnt;
++      u32 abrt_cnt;
++#if defined(SPI_DSPI)
++      u32 *mcr;                       /* DSPI MCR register */
++      u32 *ctar;                      /* DSPI CTAR register */
++      u32 *dspi_dtfr;         /* DSPI DTFR register */
++      u32 *dspi_drfr;         /* DSPI DRFR register */
++      u32 *dspi_rser;         /* DSPI RSER register */
++      u32 *dspi_sr;           /* DSPI status register */
++      u8      dspi_ctas;              /* DSPI CTAS value*/
++      
++#if defined(SPI_DSPI_EDMA)
++      void*   edma_tx_buf;
++      void*   edma_rx_buf;
++#endif
++      
++              
++#else
++      u16 *qmr;          /* QSPI mode register      */
++      u16 *qdlyr;        /* QSPI delay register     */
++      u16 *qwr;          /* QSPI wrap register      */
++      u16 *qir;          /* QSPI interrupt register */
++      u16 *qar;          /* QSPI address register   */
++      u16 *qdr;          /* QSPI data register      */
++      u16 *qcr;          /* QSPI command register   */
++#endif
++      u8  *par;          /* Pin assignment register */
++      u8  *int_icr;      /* Interrupt level and priority register */
++      u32 *int_mr;       /* Interrupt mask register */
++      void (*cs_control)(u8 cs, u8 command);
++};
++
++#define DSPI_CS(cs) ((1<<(cs))<<16)
++
++
++/****************************************************************************/
++
++/*
++ * SPI local functions
++ */
++
++//#define SPI_COLDFIRE_DEBUG
++
++static void *next_transfer(struct driver_data *drv_data)
++{
++      struct spi_message *msg = drv_data->cur_msg;
++      struct spi_transfer *trans = drv_data->cur_transfer;
++
++      /* Move to next transfer */
++      if (trans->transfer_list.next != &msg->transfers) {
++              drv_data->cur_transfer =
++                      list_entry(trans->transfer_list.next,
++                                      struct spi_transfer,
++                                      transfer_list);
++              return RUNNING_STATE;
++      } else
++              return DONE_STATE;
++}
++
++
++#define DSPI_BITS MCF_DSPI_DCTAR_FMSZ(15)
++#define DSPI_BITS_16 MCF_DSPI_DCTAR_FMSZ(15)
++#define DSPI_BITS_8 MCF_DSPI_DCTAR_FMSZ(7)
++#define DSPI_FIFO_SIZE 16
++
++static inline int is_word_transfer(struct driver_data *drv_data)
++{
++#if defined(SPI_DSPI)
++      return ((*drv_data->ctar & DSPI_BITS_16) == DSPI_BITS_8) ? 0 : 1;
++#else
++      return ((*drv_data->qmr & QMR_BITS) == QMR_BITS_8) ? 0 : 1;
++#endif
++}
++
++static void inline set_8bit_transfer_mode(struct driver_data *drv_data)
++{
++#if defined(SPI_DSPI)
++      *drv_data->ctar |= (*drv_data->ctar & ~DSPI_BITS) | DSPI_BITS_8;
++#else
++      *drv_data->qmr |= (*drv_data->qmr & ~QMR_BITS) | QMR_BITS_8;
++#endif
++}
++
++static void inline set_16bit_transfer_mode(struct driver_data *drv_data)
++{
++#if defined(SPI_DSPI)
++      *drv_data->ctar |= (*drv_data->ctar & ~DSPI_BITS) | DSPI_BITS_16;
++#else
++      *drv_data->qmr |= (*drv_data->qmr & ~QMR_BITS);
++#endif
++}
++
++static int write(struct driver_data *drv_data)
++{
++      int tx_count = 0;
++#ifndef SPI_DSPI
++      int cmd_count = 0;
++#endif
++      int tx_word;
++
++#if defined(SPI_DSPI)
++
++#if defined(SPI_DSPI_EDMA)
++      u32* edma_wr;
++#endif
++
++      u16 d16;
++      u8  d8;
++      u32 dspi_pushr;
++      int first = 1;
++#endif
++
++      tx_word = is_word_transfer(drv_data);
++
++      // If we are in word mode, but only have a single byte to transfer
++      // then switch to byte mode temporarily.  Will switch back at the
++      // end of the transfer.
++      if (tx_word && ((drv_data->tx_end - drv_data->tx) == 1)) {
++              drv_data->flags |= TRAN_STATE_WORD_ODD_NUM;
++              set_8bit_transfer_mode(drv_data);
++              tx_word = 0;
++      }
++
++
++#if defined(SPI_DSPI)
++
++#if defined(SPI_DSPI_EDMA)
++      edma_wr = (u32*)(drv_data->edma_tx_buf);
++#endif
++
++
++#if defined(SPI_DSPI_EDMA)
++      while ((drv_data->tx < drv_data->tx_end) && (tx_count < EDMA_BUFFER_SIZE)) {
++#else
++      while ((drv_data->tx < drv_data->tx_end) && (tx_count < DSPI_FIFO_SIZE)) {
++#endif
++              if (tx_word) {
++                      if ((drv_data->tx_end - drv_data->tx) == 1)
++                              break;
++                      if (!(drv_data->flags & TRAN_STATE_TX_VOID)) {
++                              d16 = *(u16 *)drv_data->tx;
++                      } else {
++                              d16 = drv_data->void_write_data;
++                      }
++
++                      dspi_pushr = MCF_DSPI_DTFR_TXDATA(d16)
++                                                | DSPI_CS(drv_data->cs)
++                                               | MCF_DSPI_DTFR_CTAS(drv_data->dspi_ctas)
++                                               //| MCF_DSPI_DTFR_CONT
++                                               ;
++
++                      drv_data->tx += 2;
++
++#if defined(SPI_DSPI_EDMA)
++                      if (drv_data->tx == drv_data->tx_end  || tx_count==EDMA_BUFFER_SIZE-1) {
++#else                 
++                      if (drv_data->tx == drv_data->tx_end  || tx_count==DSPI_FIFO_SIZE-1) {
++#endif                                
++                              // last transfer in queue
++                              dspi_pushr |= MCF_DSPI_DTFR_EOQ;
++                              if (drv_data->cs_change) {
++                                      dspi_pushr &= ~MCF_DSPI_DTFR_CONT;
++                              }
++                      }
++
++                      if (first) {
++                              first = 0;
++                              dspi_pushr |= MCF_DSPI_DTFR_CTCNT; // clear counter
++                      }
++#if defined(SPI_DSPI_EDMA)
++                      *edma_wr = dspi_pushr;
++                      edma_wr++;                      
++#else
++                      *drv_data->dspi_dtfr = dspi_pushr;
++                      //MCF_DSPI_DTFR = dspi_pushr;
++#endif
++
++
++              } else {
++                      if (!(drv_data->flags & TRAN_STATE_TX_VOID)) {
++                              d8 = *(u8 *)drv_data->tx;
++                      } else {
++                              d8 = *(u8 *)&drv_data->void_write_data;
++                      }
++
++                      dspi_pushr = MCF_DSPI_DTFR_TXDATA(d8)
++                                               | DSPI_CS(drv_data->cs)
++                                               /* | MCF_DSPI_DTFR_PCS5 | */
++                                               | MCF_DSPI_DTFR_CTAS(drv_data->dspi_ctas)
++                                               | MCF_DSPI_DTFR_CONT;
++
++                      drv_data->tx++;
++
++                      if (drv_data->tx == drv_data->tx_end  || tx_count==DSPI_FIFO_SIZE-1) {
++                              // last transfer in queue
++                              dspi_pushr |= MCF_DSPI_DTFR_EOQ;
++                              if (drv_data->cs_change) {
++                                      dspi_pushr &= ~MCF_DSPI_DTFR_CONT;
++                              }
++                      }
++
++                      if (first) {
++                              first = 0;
++                              dspi_pushr |= MCF_DSPI_DTFR_CTCNT; // clear counter
++                      }
++
++#if defined(SPI_DSPI_EDMA)
++                      *edma_wr = dspi_pushr;
++                      edma_wr++;                      
++#else                 
++                      *drv_data->dspi_dtfr = dspi_pushr;
++                      //MCF_DSPI_DTFR = dspi_pushr;
++#endif                        
++
++              }
++              tx_count++;
++      }
++
++#if defined(SPI_DSPI_EDMA)
++
++      if (tx_count>0) {
++
++              // TODO: initiate eDMA transfer
++              set_edma_params(DSPI_DMA_TX_TCD,
++#ifdef SPI_USE_MMU
++                                      virt_to_phys(drv_data->edma_tx_buf),
++#else
++                                      drv_data->edma_tx_buf,
++#endif
++                                      (u32)drv_data->dspi_dtfr,
++                                      MCF_EDMA_TCD_ATTR_SSIZE_32BIT | MCF_EDMA_TCD_ATTR_DSIZE_32BIT,
++                                      4,  // soff
++                                      4,  // nbytes
++                                      0,  // slast
++                                      tx_count, // citer
++                                      tx_count, // biter
++                                      0, // doff
++                                      0, // dlastsga
++                                      0, // major_int
++                                      1  // disable_req
++                                      );
++
++              set_edma_params(DSPI_DMA_RX_TCD,
++                                      (u32)drv_data->dspi_drfr,
++#ifdef SPI_USE_MMU
++                                      virt_to_phys(drv_data->edma_rx_buf),
++#else
++                                      drv_data->edma_rx_buf,
++#endif
++                                      MCF_EDMA_TCD_ATTR_SSIZE_32BIT | MCF_EDMA_TCD_ATTR_DSIZE_32BIT,
++                                      0,  // soff
++                                      4,  // nbytes
++                                      0,  // slast
++                                      tx_count, // citer
++                                      tx_count, // biter
++                                      4, // doff
++                                      0,  // dlastsga
++                    0,   // major_int
++                                      1       // disable_req
++                                      );
++
++
++              start_edma_transfer(DSPI_DMA_TX_TCD); // transmit SPI data
++              start_edma_transfer(DSPI_DMA_RX_TCD); // receive SPI data
++      }
++#endif
++
++#else
++
++      *drv_data->qar = QSPI_TRANSMIT_RAM;
++      while ((drv_data->tx < drv_data->tx_end) && (tx_count < QSPI_RAM_SIZE)) {
++              if (tx_word) {
++                      if ((drv_data->tx_end - drv_data->tx) == 1)
++                              break;
++
++                      if (!(drv_data->flags & TRAN_STATE_TX_VOID))
++                              *drv_data->qdr = *(u16 *)drv_data->tx;
++                      else
++                              *drv_data->qdr = drv_data->void_write_data;
++                      drv_data->tx += 2;
++              } else {
++                      if (!(drv_data->flags & TRAN_STATE_TX_VOID))
++                              *drv_data->qdr = *(u8 *)drv_data->tx;
++                      else
++                              *drv_data->qdr = *(u8 *)&drv_data->void_write_data;
++                      drv_data->tx++;
++              }
++              tx_count++;
++      }
++
++
++      *drv_data->qar = QSPI_COMMAND_RAM;
++      while (cmd_count < tx_count) {
++              u16 qcr =   QSPI_COMMAND
++                        | QCR_CONT
++                        | (~((0x01 << drv_data->cs) << 8) & 0x0F00);
++
++              if (       (cmd_count == tx_count - 1)
++                      && (drv_data->tx == drv_data->tx_end)
++                      && (drv_data->cs_change) ) {
++                      qcr &= ~QCR_CONT;
++              }
++              *drv_data->qcr = qcr;
++              cmd_count++;
++      }
++
++      *drv_data->qwr = (*drv_data->qwr & ~QWR_ENDQP_MASK) | ((cmd_count - 1) << 8);
++
++      /* Fire it up! */
++      *drv_data->qdlyr |= QDLYR_SPE;
++#endif
++
++      return tx_count;
++}
++
++
++static int read(struct driver_data *drv_data)
++{
++      int rx_count = 0;
++      int rx_word;
++#if defined(SPI_DSPI_EDMA)
++      u32* rx_edma;
++#endif
++      u16 d;
++      rx_word = is_word_transfer(drv_data);
++
++#if defined(SPI_DSPI)
++
++#if defined(SPI_DSPI_EDMA)
++      rx_edma = (u32*) drv_data->edma_tx_buf;
++      while ((drv_data->rx < drv_data->rx_end) && (rx_count < EDMA_BUFFER_SIZE)) {
++#else
++      while ((drv_data->rx < drv_data->rx_end) && (rx_count < DSPI_FIFO_SIZE)) {
++#endif
++              if (rx_word) {
++                      if ((drv_data->rx_end - drv_data->rx) == 1)
++                              break;
++#if defined(SPI_DSPI_EDMA)
++                      d = MCF_DSPI_DRFR_RXDATA(*rx_edma);
++                      rx_edma++;
++#else
++                      d = MCF_DSPI_DRFR_RXDATA(*drv_data->dspi_drfr);
++#endif
++
++                      if (!(drv_data->flags & TRAN_STATE_RX_VOID))
++                              *(u16 *)drv_data->rx = d;
++                      drv_data->rx += 2;
++              } else {
++#if defined(SPI_DSPI_EDMA)
++                      d = MCF_DSPI_DRFR_RXDATA(*rx_edma);
++                      rx_edma++;
++#else
++                      d = MCF_DSPI_DRFR_RXDATA(*drv_data->dspi_drfr);
++#endif
++                      if (!(drv_data->flags & TRAN_STATE_RX_VOID))
++                              *(u8 *)drv_data->rx = d;
++                      drv_data->rx++;
++              }
++              rx_count++;
++      }
++
++
++#else
++
++      *drv_data->qar = QSPI_RECEIVE_RAM;
++      while ((drv_data->rx < drv_data->rx_end) && (rx_count < QSPI_RAM_SIZE)) {
++              if (rx_word) {
++                      if ((drv_data->rx_end - drv_data->rx) == 1)
++                              break;
++
++                      if (!(drv_data->flags & TRAN_STATE_RX_VOID))
++                              *(u16 *)drv_data->rx = *drv_data->qdr;
++                      drv_data->rx += 2;
++              } else {
++                      if (!(drv_data->flags & TRAN_STATE_RX_VOID))
++                              *(u8 *)drv_data->rx = *drv_data->qdr;
++                      drv_data->rx++;
++              }
++              rx_count++;
++      }
++#endif
++
++      return rx_count;
++}
++
++
++static inline void qspi_setup_chip(struct driver_data *drv_data)
++{
++      struct chip_data *chip = drv_data->cur_chip;
++
++#if defined(SPI_DSPI)
++
++      *drv_data->mcr = chip->mcr_val;
++
++      // TODO: remove later
++      chip->ctar_val = 0x78560118;
++
++      *drv_data->ctar = chip->ctar_val;
++      *drv_data->dspi_rser =  0
++                                                      | MCF_DSPI_DRSER_EOQFE
++#if defined(SPI_DSPI_EDMA)
++                                                      | MCF_DSPI_DRSER_TFFFE
++                                                      | MCF_DSPI_DRSER_TFFFS
++#endif
++                                                      ;
++
++
++#else
++      *drv_data->qmr = chip->qmr_val;
++      *drv_data->qdlyr = chip->qdlyr_val;
++      *drv_data->qwr = chip->qwr_val;
++
++      /*
++       * Enable all the interrupts and clear all the flags
++       */
++      *drv_data->qir =  (QIR_SPIFE | QIR_ABRTE | QIR_WCEFE)
++                      | (QIR_WCEFB | QIR_ABRTB | QIR_ABRTL)
++                      | (QIR_SPIF  | QIR_ABRT  | QIR_WCEF);
++#endif
++}
++
++#if defined(SPI_DSPI_EDMA)
++static int edma_tx_handler(int channel, void* dev)
++{
++      if (channel == DSPI_DMA_TX_TCD) {
++              stop_edma_transfer(DSPI_DMA_TX_TCD);
++      }
++      return IRQ_HANDLED;
++}
++
++static int edma_rx_handler(int channel, void* dev)
++{
++      if (channel == DSPI_DMA_RX_TCD) {
++              stop_edma_transfer(DSPI_DMA_RX_TCD);
++      }
++
++      return IRQ_HANDLED;
++}
++#endif
++
++static irqreturn_t qspi_interrupt(int irq, void *dev_id)
++{
++      struct driver_data *drv_data = (struct driver_data *)dev_id;
++      struct spi_message *msg = drv_data->cur_msg;
++#if defined(SPI_DSPI)
++#if !defined(SPI_DSPI_EDMA)
++      u32 irq_status = *drv_data->dspi_sr;
++#endif
++#else
++      u16 irq_status = *drv_data->qir;
++#endif
++
++      /* Clear all flags immediately */
++#if defined(SPI_DSPI)
++      *drv_data->dspi_sr = MCF_DSPI_DSR_EOQF;
++#else
++      *drv_data->qir |= (QIR_SPIF | QIR_ABRT | QIR_WCEF);
++#endif
++
++      if (!drv_data->cur_msg || !drv_data->cur_msg->state) {
++#if !defined(SPI_DSPI_EDMA)
++              /* if eDMA is used it happens some time (at least once)*/
++              printk(KERN_ERR "coldfire-qspi: bad message or transfer "
++              "state in interrupt handler. IRQ status=%x\n", irq_status);
++#endif
++              return IRQ_NONE;
++      }
++
++#if !defined(SPI_DSPI)
++      if (irq_status & QIR_SPIF) {
++#endif
++              /*
++               * Read the data into the buffer and reload and start
++               * queue with new data if not finished.  If finished
++               * then setup the next transfer
++               */
++               read(drv_data);
++
++               if (drv_data->rx == drv_data->rx_end) {
++                      /*
++                       * Finished now - fall through and schedule next
++                       * transfer tasklet
++                       */
++                      if (drv_data->flags & TRAN_STATE_WORD_ODD_NUM) {
++                              //*drv_data->qmr &= ~QMR_BITS;
++                              set_16bit_transfer_mode(drv_data);
++                      }
++
++                      msg->state = next_transfer(drv_data);
++                      msg->actual_length += drv_data->len;
++               } else {
++                      /* not finished yet - keep going */
++                      write(drv_data);
++                      return IRQ_HANDLED;
++               }
++#if !defined(SPI_DSPI)
++      } else {
++              if (irq_status & QIR_WCEF)
++                      drv_data->wce_cnt++;
++
++              if (irq_status & QIR_ABRT)
++                      drv_data->abrt_cnt++;
++
++              msg->state = ERROR_STATE;
++      }
++#endif
++
++      tasklet_schedule(&drv_data->pump_transfers);
++
++      return IRQ_HANDLED;
++}
++
++/* caller already set message->status; dma and pio irqs are blocked */
++static void giveback(struct driver_data *drv_data)
++{
++      struct spi_transfer* last_transfer;
++      unsigned long flags;
++      struct spi_message *msg;
++
++      spin_lock_irqsave(&drv_data->lock, flags);
++      msg = drv_data->cur_msg;
++      drv_data->cur_msg = NULL;
++      drv_data->cur_transfer = NULL;
++      drv_data->cur_chip = NULL;
++      queue_work(drv_data->workqueue, &drv_data->pump_messages);
++      spin_unlock_irqrestore(&drv_data->lock, flags);
++
++      last_transfer = list_entry(msg->transfers.prev,
++                                      struct spi_transfer,
++                                      transfer_list);
++
++      if (!last_transfer->cs_change)
++              drv_data->cs_control(drv_data->cs, QSPI_CS_DROP);
++
++      msg->state = NULL;
++      if (msg->complete)
++              msg->complete(msg->context);
++}
++
++
++static void pump_transfers(unsigned long data)
++{
++      struct driver_data *drv_data = (struct driver_data *)data;
++      struct spi_message *message = NULL;
++      struct spi_transfer *transfer = NULL;
++      struct spi_transfer *previous = NULL;
++      struct chip_data *chip = NULL;
++      unsigned long flags;
++
++      /* Get current state information */
++      message = drv_data->cur_msg;
++      transfer = drv_data->cur_transfer;
++      chip = drv_data->cur_chip;
++
++      /* Handle for abort */
++      if (message->state == ERROR_STATE) {
++              message->status = -EIO;
++              giveback(drv_data);
++              return;
++      }
++
++      /* Handle end of message */
++      if (message->state == DONE_STATE) {
++              message->status = 0;
++              giveback(drv_data);
++              return;
++      }
++
++      if (message->state == START_STATE) {
++              qspi_setup_chip(drv_data);
++
++              if (drv_data->cs_control) {
++                      //printk( "m s\n" );
++                      drv_data->cs_control(message->spi->chip_select, QSPI_CS_ASSERT);
++              }
++      }
++
++      /* Delay if requested at end of transfer*/
++      if (message->state == RUNNING_STATE) {
++              previous = list_entry(transfer->transfer_list.prev,
++                                      struct spi_transfer,
++                                      transfer_list);
++
++              if (drv_data->cs_control && transfer->cs_change)
++                      drv_data->cs_control(message->spi->chip_select, QSPI_CS_DROP);
++
++              if (previous->delay_usecs)
++                      udelay(previous->delay_usecs);
++
++              if (drv_data->cs_control && transfer->cs_change)
++                      drv_data->cs_control(message->spi->chip_select, QSPI_CS_ASSERT);
++      }
++
++      drv_data->flags = 0;
++      drv_data->tx = (void *)transfer->tx_buf;
++      drv_data->tx_end = drv_data->tx + transfer->len;
++      drv_data->rx = transfer->rx_buf;
++      drv_data->rx_end = drv_data->rx + transfer->len;
++      drv_data->len = transfer->len;
++      if (!drv_data->rx)
++              drv_data->flags |= TRAN_STATE_RX_VOID;
++      if (!drv_data->tx)
++              drv_data->flags |= TRAN_STATE_TX_VOID;
++      drv_data->cs = message->spi->chip_select;
++      drv_data->cs_change = transfer->cs_change;
++      drv_data->void_write_data = chip->void_write_data;
++
++      message->state = RUNNING_STATE;
++
++      /* Go baby, go */
++      local_irq_save(flags);
++      write(drv_data);
++      local_irq_restore(flags);
++}
++
++
++static void pump_messages(struct work_struct * work)
++{
++      struct driver_data *drv_data;
++      unsigned long flags;
++
++      drv_data = container_of(work,  struct driver_data, pump_messages);
++
++      /* Lock queue and check for queue work */
++      spin_lock_irqsave(&drv_data->lock, flags);
++      if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) {
++              drv_data->busy = 0;
++              spin_unlock_irqrestore(&drv_data->lock, flags);
++              return;
++      }
++
++      /* Make sure we are not already running a message */
++      if (drv_data->cur_msg) {
++              spin_unlock_irqrestore(&drv_data->lock, flags);
++              return;
++      }
++
++      /* Extract head of queue */
++      drv_data->cur_msg = list_entry(drv_data->queue.next,
++                                      struct spi_message, queue);
++      list_del_init(&drv_data->cur_msg->queue);
++
++      /* Initial message state*/
++      drv_data->cur_msg->state = START_STATE;
++      drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
++                                              struct spi_transfer,
++                                              transfer_list);
++
++      /* Setup the SPI Registers using the per chip configuration */
++      drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);
++
++      /* Mark as busy and launch transfers */
++      tasklet_schedule(&drv_data->pump_transfers);
++
++      drv_data->busy = 1;
++      spin_unlock_irqrestore(&drv_data->lock, flags);
++}
++
++/****************************************************************************/
++
++/*
++ * SPI master implementation
++ */
++
++static int transfer(struct spi_device *spi, struct spi_message *msg)
++{
++      struct driver_data *drv_data = spi_master_get_devdata(spi->master);
++      unsigned long flags;
++
++      spin_lock_irqsave(&drv_data->lock, flags);
++
++      if (drv_data->run == QUEUE_STOPPED) {
++              spin_unlock_irqrestore(&drv_data->lock, flags);
++              return -ESHUTDOWN;
++      }
++
++      msg->actual_length = 0;
++      msg->status = -EINPROGRESS;
++      msg->state = START_STATE;
++
++      list_add_tail(&msg->queue, &drv_data->queue);
++
++      if (drv_data->run == QUEUE_RUNNING && !drv_data->busy)
++              queue_work(drv_data->workqueue, &drv_data->pump_messages);
++
++      spin_unlock_irqrestore(&drv_data->lock, flags);
++
++      return 0;
++}
++
++
++static int setup(struct spi_device *spi)
++{
++      struct coldfire_spi_chip *chip_info;
++      struct chip_data *chip;
++#ifndef SPI_DSPI
++      u32 baud_divisor = 255;
++#endif
++
++      chip_info = (struct coldfire_spi_chip *)spi->controller_data;
++
++      /* Only alloc on first setup */
++      chip = spi_get_ctldata(spi);
++      if (chip == NULL) {
++              chip = kcalloc(1, sizeof(struct chip_data), GFP_KERNEL);
++              if (!chip)
++                      return -ENOMEM;
++              spi->mode = chip_info->mode;
++              spi->bits_per_word = chip_info->bits_per_word;
++      }
++
++#if defined(SPI_DSPI)
++      chip->mcr.master = 1;
++      chip->mcr.cont_scke = 0;
++      chip->mcr.dconf = 0;
++      chip->mcr.frz = 0;
++      chip->mcr.mtfe = 1;
++      chip->mcr.pcsse = 0;
++      chip->mcr.rooe = 0;
++      chip->mcr.pcsis = 0xFF;
++      chip->mcr.reserved15 = 0;
++      chip->mcr.mdis = 0;
++      chip->mcr.dis_tx = 0;
++      chip->mcr.dis_rxf = 0;
++      chip->mcr.clr_tx = 1;
++      chip->mcr.clr_rxf = 1;
++      chip->mcr.smpl_pt = 0;
++      chip->mcr.reserved71 = 0;
++      chip->mcr.halt = 0;
++
++      if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) {
++              chip->ctar.fmsz = spi->bits_per_word-1;
++      } else {
++              printk(KERN_ERR "coldfire-qspi: invalid wordsize\n");
++              kfree(chip);
++              return -ENODEV;
++      }
++
++      if (spi->mode & SPI_CPHA)
++              chip->ctar.cpha = 1;
++      else
++              chip->ctar.cpha = 0;
++
++      if (spi->mode & SPI_CPOL)
++              chip->ctar.cpol = 1;
++      else
++              chip->ctar.cpol = 0;
++
++      if (spi->mode & SPI_LSB_FIRST)
++              chip->ctar.lsbfe = 1;
++      else
++              chip->ctar.lsbfe = 0;
++
++      /* This values are default for audio device */
++      chip->ctar.dbr = 0;
++      chip->ctar.pbr = 2;
++      chip->ctar.br = 8;
++
++      /* This values are default for audio device */
++      chip->ctar.pcssck = 1;
++      chip->ctar.pasc = 1;
++      chip->ctar.pdt = 1;
++      chip->ctar.cssck = 0;
++      chip->ctar.asc = 1;
++      chip->ctar.dt = 1;
++
++      chip->void_write_data = chip_info->void_write_data;
++
++#else
++
++      chip->qwr.csiv = 1;    // Chip selects are active low
++      chip->qmr.master = 1;  // Must set to master mode
++      chip->qmr.dohie = 1;   // Data output high impediance enabled
++      chip->void_write_data = chip_info->void_write_data;
++
++      chip->qdlyr.qcd = chip_info->del_cs_to_clk;
++      chip->qdlyr.dtl = chip_info->del_after_trans;
++
++      if (spi->max_speed_hz != 0)
++              baud_divisor = (MCF_CLK/(2*spi->max_speed_hz));
++
++      if (baud_divisor < 2)
++              baud_divisor = 2;
++
++      if (baud_divisor > 255)
++              baud_divisor = 255;
++
++      chip->qmr.baud = baud_divisor;
++
++      //printk( "QSPI: spi->max_speed_hz %d\n", spi->max_speed_hz );
++      //printk( "QSPI: Baud set to %d\n", chip->qmr.baud );
++
++      if (spi->mode & SPI_CPHA)
++              chip->qmr.cpha = 1;
++
++      if (spi->mode & SPI_CPOL)
++              chip->qmr.cpol = 1;
++
++      if (spi->bits_per_word == 16) {
++              chip->qmr.bits = 0;
++      } else if ((spi->bits_per_word >= 8) && (spi->bits_per_word <= 15)) {
++              chip->qmr.bits = spi->bits_per_word;
++      } else {
++              printk(KERN_ERR "coldfire-qspi: invalid wordsize\n");
++              kfree(chip);
++              return -ENODEV;
++      }
++
++#endif
++
++      spi_set_ctldata(spi, chip);
++
++      return 0;
++}
++
++static int init_queue(struct driver_data *drv_data)
++{
++      INIT_LIST_HEAD(&drv_data->queue);
++      spin_lock_init(&drv_data->lock);
++
++      drv_data->run = QUEUE_STOPPED;
++      drv_data->busy = 0;
++
++      tasklet_init(&drv_data->pump_transfers,
++                      pump_transfers, (unsigned long)drv_data);
++
++      INIT_WORK(&drv_data->pump_messages, pump_messages/*, drv_data*/);
++
++      drv_data->workqueue = create_singlethread_workqueue(
++                                      drv_data->master->cdev.dev->bus_id);
++      if (drv_data->workqueue == NULL)
++              return -EBUSY;
++
++      return 0;
++}
++
++static int start_queue(struct driver_data *drv_data)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&drv_data->lock, flags);
++
++      if (drv_data->run == QUEUE_RUNNING || drv_data->busy) {
++              spin_unlock_irqrestore(&drv_data->lock, flags);
++              return -EBUSY;
++      }
++
++      drv_data->run = QUEUE_RUNNING;
++      drv_data->cur_msg = NULL;
++      drv_data->cur_transfer = NULL;
++      drv_data->cur_chip = NULL;
++      spin_unlock_irqrestore(&drv_data->lock, flags);
++
++      queue_work(drv_data->workqueue, &drv_data->pump_messages);
++
++      return 0;
++}
++
++static int stop_queue(struct driver_data *drv_data)
++{
++      unsigned long flags;
++      unsigned limit = 500;
++      int status = 0;
++
++      spin_lock_irqsave(&drv_data->lock, flags);
++
++      /* This is a bit lame, but is optimized for the common execution path.
++       * A wait_queue on the drv_data->busy could be used, but then the common
++       * execution path (pump_messages) would be required to call wake_up or
++       * friends on every SPI message. Do this instead */
++      drv_data->run = QUEUE_STOPPED;
++      while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) {
++              spin_unlock_irqrestore(&drv_data->lock, flags);
++              msleep(10);
++              spin_lock_irqsave(&drv_data->lock, flags);
++      }
++
++      if (!list_empty(&drv_data->queue) || drv_data->busy)
++              status = -EBUSY;
++
++      spin_unlock_irqrestore(&drv_data->lock, flags);
++
++      return status;
++}
++
++static int destroy_queue(struct driver_data *drv_data)
++{
++      int status;
++
++      status = stop_queue(drv_data);
++      if (status != 0)
++              return status;
++
++      destroy_workqueue(drv_data->workqueue);
++
++      return 0;
++}
++
++
++static void cleanup(const struct spi_device *spi)
++{
++      struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi);
++
++      dev_dbg(&spi->dev, "spi_device %u.%u cleanup\n",
++              spi->master->bus_num, spi->chip_select);
++
++      kfree(chip);
++}
++
++
++/****************************************************************************/
++
++/*
++ * Generic Device driver routines and interface implementation
++ */
++
++static int coldfire_spi_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct coldfire_spi_master *platform_info;
++      struct spi_master *master;
++      struct driver_data *drv_data = 0;
++      struct resource *memory_resource;
++      int irq;
++      int status = 0;
++      int i;
++
++#if defined(SPI_DSPI_EDMA)
++      init_edma();
++#endif
++
++      platform_info = (struct coldfire_spi_master *)pdev->dev.platform_data;
++
++      master = spi_alloc_master(dev, sizeof(struct driver_data));
++      if (!master)
++              return -ENOMEM;
++
++      drv_data = class_get_devdata(&master->cdev);
++      drv_data->master = master;
++
++      INIT_LIST_HEAD(&drv_data->queue);
++      spin_lock_init(&drv_data->lock);
++
++      master->bus_num = platform_info->bus_num;
++      master->num_chipselect = platform_info->num_chipselect;
++      master->cleanup = cleanup;
++      master->setup = setup;
++      master->transfer = transfer;
++
++      drv_data->cs_control = platform_info->cs_control;
++      if (drv_data->cs_control)
++              for(i = 0; i < master->num_chipselect; i++)
++                      drv_data->cs_control(i, QSPI_CS_INIT | QSPI_CS_DROP);
++
++      /* Setup register addresses */
++      memory_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi-module");
++      if (!memory_resource) {
++              dev_dbg(dev, "can not find platform module memory\n");
++              goto out_error_master_alloc;
++      }
++
++#if defined(SPI_DSPI_EDMA)
++      drv_data->edma_tx_buf = kmalloc(EDMA_BUFSIZE_KMALLOC, GFP_DMA);
++      if (!drv_data->edma_tx_buf) {
++              dev_dbg(dev, "cannot allocate eDMA TX memory\n");
++              goto out_error_master_alloc;
++      }
++      drv_data->edma_rx_buf = kmalloc(EDMA_BUFSIZE_KMALLOC, GFP_DMA);
++      if (!drv_data->edma_rx_buf) {
++              kfree(drv_data->edma_tx_buf);
++              dev_dbg(dev, "cannot allocate eDMA RX memory\n");
++              goto out_error_master_alloc;
++      }
++#endif        
++      
++#if defined(SPI_DSPI)
++
++      drv_data->mcr           = (void *)(memory_resource->start + 0x00000000);
++      drv_data->ctar          = (void *)(memory_resource->start + 0x0000000C);
++      drv_data->dspi_sr       = (void *)(memory_resource->start + 0x0000002C);
++      drv_data->dspi_rser = (void *)(memory_resource->start + 0x00000030);
++      drv_data->dspi_dtfr = (void *)(memory_resource->start + 0x00000034);
++      drv_data->dspi_drfr = (void *)(memory_resource->start + 0x00000038);
++
++#else
++
++      drv_data->qmr   = (void *)(memory_resource->start + 0x00000000);
++      drv_data->qdlyr = (void *)(memory_resource->start + 0x00000004);
++      drv_data->qwr   = (void *)(memory_resource->start + 0x00000008);
++      drv_data->qir   = (void *)(memory_resource->start + 0x0000000c);
++      drv_data->qar   = (void *)(memory_resource->start + 0x00000010);
++      drv_data->qdr   = (void *)(memory_resource->start + 0x00000014);
++      drv_data->qcr   = (void *)(memory_resource->start + 0x00000014);
++
++#endif
++
++      /* Setup register addresses */
++      memory_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi-par");
++      if (!memory_resource) {
++              dev_dbg(dev, "can not find platform par memory\n");
++              goto out_error_master_alloc;
++      }
++
++      drv_data->par = (void *)memory_resource->start;
++
++      /* Setup register addresses */
++      memory_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi-int-level");
++      if (!memory_resource) {
++              dev_dbg(dev, "can not find platform par memory\n");
++              goto out_error_master_alloc;
++      }
++
++      drv_data->int_icr = (void *)memory_resource->start;
++
++      /* Setup register addresses */
++      memory_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi-int-mask");
++      if (!memory_resource) {
++              dev_dbg(dev, "can not find platform par memory\n");
++              goto out_error_master_alloc;
++      }
++
++      drv_data->int_mr = (void *)memory_resource->start;
++
++      irq = platform_info->irq_vector;
++
++      status = request_irq(platform_info->irq_vector, qspi_interrupt, SA_INTERRUPT, dev->bus_id, drv_data);
++      if (status < 0) {
++              dev_err(&pdev->dev, "unable to attach ColdFire QSPI interrupt\n");
++              goto out_error_master_alloc;
++      }
++
++      /* Now that we have all the addresses etc.  Let's set it up */
++      // TODO:
++      //*drv_data->par = platform_info->par_val;
++
++      MCF_GPIO_PAR_DSPI = 0
++              | MCF_GPIO_PAR_DSPI_PCS5_PCS5
++              | MCF_GPIO_PAR_DSPI_PCS2_PCS2
++              | MCF_GPIO_PAR_DSPI_PCS1_PCS1
++              | MCF_GPIO_PAR_DSPI_PCS0_PCS0
++              | MCF_GPIO_PAR_DSPI_SIN_SIN
++              | MCF_GPIO_PAR_DSPI_SOUT_SOUT
++              | MCF_GPIO_PAR_DSPI_SCK_SCK;
++
++      *drv_data->int_icr = platform_info->irq_lp;
++      *drv_data->int_mr &= ~platform_info->irq_mask;
++
++#ifdef SPI_DSPI
++      drv_data->dspi_ctas = 0; // TODO: change later
++#endif
++
++      /* Initial and start queue */
++      status = init_queue(drv_data);
++      if (status != 0) {
++              dev_err(&pdev->dev, "problem initializing queue\n");
++              goto out_error_irq_alloc;
++      }
++      status = start_queue(drv_data);
++      if (status != 0) {
++              dev_err(&pdev->dev, "problem starting queue\n");
++              goto out_error_irq_alloc;
++      }
++
++      /* Register with the SPI framework */
++      platform_set_drvdata(pdev, drv_data);
++      status = spi_register_master(master);
++      if (status != 0) {
++              dev_err(&pdev->dev, "problem registering spi master\n");
++              status = -EINVAL;
++                goto out_error_queue_alloc;
++      }
++
++#if defined(SPI_DSPI_EDMA)
++      if (request_edma_channel(DSPI_DMA_TX_TCD,
++                                                      edma_tx_handler,
++                                                      NULL,
++                                                      pdev,
++                                                      NULL, /* spinlock */
++                                                      DRIVER_NAME
++                                                      )!=0)
++      {
++              dev_err(&pdev->dev, "problem requesting edma transmit channel\n");
++              status = -EINVAL;
++        goto out_error_queue_alloc;
++      }
++
++      if (request_edma_channel(DSPI_DMA_RX_TCD,
++                                                      edma_rx_handler,
++                                                      NULL,
++                                                      pdev,
++                                                      NULL, /* spinlock */
++                                                      DRIVER_NAME
++                                                      )!=0)
++      {
++              dev_err(&pdev->dev, "problem requesting edma receive channel\n");
++              status = -EINVAL;
++        goto out_edma_transmit;
++      }
++#endif
++
++      printk( "SPI: Coldfire master initialized\n" );
++      //dev_info(&pdev->dev, "driver initialized\n");
++      return status;
++
++#if defined(SPI_DSPI_EDMA)
++out_edma_transmit:
++      free_edma_channel(DSPI_DMA_TX_TCD, pdev);
++#endif
++
++out_error_queue_alloc:
++      destroy_queue(drv_data);
++
++out_error_irq_alloc:
++      free_irq(irq, drv_data);
++
++out_error_master_alloc:
++      spi_master_put(master);
++      return status;
++
++}
++
++static int coldfire_spi_remove(struct platform_device *pdev)
++{
++      struct driver_data *drv_data = platform_get_drvdata(pdev);
++      int irq;
++      int status = 0;
++
++      if (!drv_data)
++              return 0;
++
++#if defined(SPI_DSPI_EDMA)
++      free_edma_channel(DSPI_DMA_TX_TCD, pdev);
++      free_edma_channel(DSPI_DMA_RX_TCD, pdev);
++#endif        
++
++      /* Remove the queue */
++      status = destroy_queue(drv_data);
++      if (status != 0)
++              return status;
++
++      /* Disable the SSP at the peripheral and SOC level */
++      /*write_SSCR0(0, drv_data->ioaddr);
++      pxa_set_cken(drv_data->master_info->clock_enable, 0);*/
++
++      /* Release DMA */
++      /*if (drv_data->master_info->enable_dma) {
++              if (drv_data->ioaddr == SSP1_VIRT) {
++                      DRCMRRXSSDR = 0;
++                      DRCMRTXSSDR = 0;
++              } else if (drv_data->ioaddr == SSP2_VIRT) {
++                      DRCMRRXSS2DR = 0;
++                      DRCMRTXSS2DR = 0;
++              } else if (drv_data->ioaddr == SSP3_VIRT) {
++                      DRCMRRXSS3DR = 0;
++                      DRCMRTXSS3DR = 0;
++              }
++              pxa_free_dma(drv_data->tx_channel);
++              pxa_free_dma(drv_data->rx_channel);
++      }*/
++
++      /* Release IRQ */
++      irq = platform_get_irq(pdev, 0);
++      if (irq >= 0)
++              free_irq(irq, drv_data);
++
++      /* Disconnect from the SPI framework */
++      spi_unregister_master(drv_data->master);
++
++      /* Prevent double remove */
++      platform_set_drvdata(pdev, NULL);
++
++      return 0;
++}
++
++static void coldfire_spi_shutdown(struct platform_device *pdev)
++{
++      int status = 0;
++
++      if ((status = coldfire_spi_remove(pdev)) != 0)
++              dev_err(&pdev->dev, "shutdown failed with %d\n", status);
++}
++
++
++#ifdef CONFIG_PM
++static int suspend_devices(struct device *dev, void *pm_message)
++{
++      pm_message_t *state = pm_message;
++
++      if (dev->power.power_state.event != state->event) {
++              dev_warn(dev, "pm state does not match request\n");
++              return -1;
++      }
++
++      return 0;
++}
++
++static int coldfire_spi_suspend(struct platform_device *pdev, pm_message_t state)
++{
++      struct driver_data *drv_data = platform_get_drvdata(pdev);
++      int status = 0;
++
++      /* Check all childern for current power state */
++      if (device_for_each_child(&pdev->dev, &state, suspend_devices) != 0) {
++              dev_warn(&pdev->dev, "suspend aborted\n");
++              return -1;
++      }
++
++      status = stop_queue(drv_data);
++      if (status != 0)
++              return status;
++      /*write_SSCR0(0, drv_data->ioaddr);
++      pxa_set_cken(drv_data->master_info->clock_enable, 0);*/
++
++      return 0;
++}
++
++static int coldfire_spi_resume(struct platform_device *pdev)
++{
++      struct driver_data *drv_data = platform_get_drvdata(pdev);
++      int status = 0;
++
++      /* Enable the SSP clock */
++      /*pxa_set_cken(drv_data->master_info->clock_enable, 1);*/
++
++      /* Start the queue running */
++      status = start_queue(drv_data);
++      if (status != 0) {
++              dev_err(&pdev->dev, "problem starting queue (%d)\n", status);
++              return status;
++      }
++
++      return 0;
++}
++#else
++#define coldfire_spi_suspend NULL
++#define coldfire_spi_resume NULL
++#endif /* CONFIG_PM */
++
++static struct platform_driver driver = {
++      .driver = {
++              .name = "spi_coldfire",
++              .bus = &platform_bus_type,
++              .owner = THIS_MODULE,
++      },
++      .probe = coldfire_spi_probe,
++      .remove = __devexit_p(coldfire_spi_remove),
++      .shutdown = coldfire_spi_shutdown,
++      .suspend = coldfire_spi_suspend,
++      .resume = coldfire_spi_resume,
++};
++
++static int __init coldfire_spi_init(void)
++{
++      platform_driver_register(&driver);
++
++      return 0;
++}
++module_init(coldfire_spi_init);
++
++static void __exit coldfire_spi_exit(void)
++{
++      platform_driver_unregister(&driver);
++}
++module_exit(coldfire_spi_exit);
+--- /dev/null
++++ b/drivers/spi/ssi_audio.c
+@@ -0,0 +1,906 @@
++/*
++ * MCF5445x audio driver.
++ *
++ * Yaroslav Vinogradov yaroslav.vinogradov@freescale.com
++ * Copyright Freescale Semiconductor, Inc. 2006
++ *
++ * 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/init.h>
++#include <linux/delay.h>
++#include <linux/spi/spi.h>
++#include <linux/fs.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <asm/mcfsim.h>
++#include <linux/interrupt.h>
++#include <linux/soundcard.h>
++#include <asm/uaccess.h>
++#include <asm/virtconvert.h>
++
++#include <asm/coldfire.h>
++#include <asm/coldfire_edma.h>
++#include <asm/mcf5445x_ssi.h>
++#include <asm/mcf5445x_ccm.h>
++#include <asm/mcf5445x_gpio.h>
++
++#define SOUND_DEVICE_NAME "sound"
++#define DRIVER_NAME "ssi_audio"
++
++
++/* #define AUDIO_DEBUG */
++
++#ifdef CONFIG_MMU
++#define USE_MMU
++#endif
++
++#define MAX_SPEED_HZ 12000000
++
++#define M5445x_AUDIO_IRQ_SOURCE 49
++#define M5445x_AUDIO_IRQ_VECTOR (128+M5445x_AUDIO_IRQ_SOURCE)
++#define M5445x_AUDIO_IRQ_LEVEL        5
++
++/* TLV320DAC23 audio chip registers */
++
++#define CODEC_LEFT_IN_REG                     (0x00)
++#define CODEC_RIGHT_IN_REG                    (0x01)
++#define CODEC_LEFT_HP_VOL_REG         (0x02)
++#define CODEC_RIGHT_HP_VOL_REG                (0x03)
++#define CODEC_ANALOG_APATH_REG                (0x04)
++#define CODEC_DIGITAL_APATH_REG               (0x05)
++#define CODEC_POWER_DOWN_REG          (0x06)
++#define CODEC_DIGITAL_IF_FMT_REG      (0x07)
++#define CODEC_SAMPLE_RATE_REG         (0x08)
++#define CODEC_DIGITAL_IF_ACT_REG      (0x09)
++#define CODEC_RESET_REG                               (0x0f)
++
++#define CODEC_SAMPLE_8KHZ             (0x0C)
++#define CODEC_SAMPLE_16KHZ            (0x58)
++#define CODEC_SAMPLE_22KHZ            (0x62)
++#define CODEC_SAMPLE_32KHZ            (0x18)
++#define CODEC_SAMPLE_44KHZ            (0x22)
++#define CODEC_SAMPLE_48KHZ            (0x00)
++
++/* Audio buffer data size */
++#define       BUFSIZE         (64*1024)
++/* DMA transfer size */
++#define DMASIZE               (16*1024)
++
++/* transmit eDMA channel for SSI channel 0 */
++#define DMA_TCD       10
++/* transmit eDMA channel for SSI channel 1 */
++#define DMA_TCD2      11
++
++struct ssi_audio {
++      struct spi_device       *spi;
++      u32 speed;
++      u32 stereo;
++      u32 bits;
++      u32 format;
++      u8  isopen;
++      u8  dmaing;
++      u8  ssi_enabled;
++      u8  channel;
++      spinlock_t lock;
++      u8* audio_buf;
++};
++
++static struct ssi_audio* audio_device = NULL;
++volatile u32 audio_start;
++volatile u32 audio_count;
++volatile u32 audio_append;
++volatile u32 audio_appstart;
++volatile u32 audio_txbusy;
++
++struct ssi_audio_format {
++      unsigned int    format;
++      unsigned int    bits;
++} ssi_audio_formattable[] = {
++      { AFMT_MU_LAW,          8 },
++      { AFMT_A_LAW,           8 },
++      { AFMT_IMA_ADPCM,       8 },
++      { AFMT_U8,              8 },
++      { AFMT_S16_LE,          16 },
++      { AFMT_S16_BE,          16 },
++      { AFMT_S8,              8 },
++      { AFMT_U16_LE,          16 },
++      { AFMT_U16_BE,          16 },
++};
++
++#define       FORMATSIZE      (sizeof(ssi_audio_formattable) / sizeof(struct ssi_audio_format))
++
++static void ssi_audio_setsamplesize(int val)
++{
++      int     i;
++
++      if (audio_device == NULL) return;
++
++      for (i = 0; (i < FORMATSIZE); i++) {
++              if (ssi_audio_formattable[i].format == val) {
++                      audio_device->format = ssi_audio_formattable[i].format;
++                      audio_device->bits = ssi_audio_formattable[i].bits;
++                      break;
++              }
++      }
++
++#ifdef AUDIO_DEBUG
++      printk(DRIVER_NAME ":ssi_audio_setsamplesize %d %d\n", audio_device->format, audio_device->bits);
++#endif
++}
++
++static void ssi_audio_txdrain(void)
++{
++#ifdef AUDIO_DEBUG
++      printk(DRIVER_NAME ":ssi_audio_txdrain()\n");
++#endif
++
++      if (audio_device == NULL) return;
++
++      while (!signal_pending(current)) {
++              if (audio_txbusy == 0)
++                      break;
++              current->state = TASK_INTERRUPTIBLE;
++              schedule_timeout(1);
++      }
++}
++
++#ifdef CONFIG_SSIAUDIO_USE_EDMA
++/*
++ *    Configure and start DMA engine.
++ */
++void __inline__ ssi_audio_dmarun(void)
++{
++      set_edma_params(DMA_TCD,
++#ifdef USE_MMU
++                                      virt_to_phys(&(audio_device->audio_buf[audio_start])),
++#else
++                                      (u32)&(audio_device->audio_buf[audio_start]),
++#endif
++                                      (u32)&MCF_SSI_TX0,
++                                      MCF_EDMA_TCD_ATTR_SSIZE_32BIT | MCF_EDMA_TCD_ATTR_DSIZE_32BIT,
++                                      8,
++                                      4,
++                                      0,
++                                      audio_count/8,
++                                      audio_count/8,
++                                      0,
++                                      0,
++                                      0, // major_int
++                                      0  // disable_req
++                                      );
++
++      set_edma_params(DMA_TCD2,
++#ifdef USE_MMU
++                                      virt_to_phys(&(audio_device->audio_buf[audio_start+4])),
++#else
++                                      (u32)&(audio_device->audio_buf[audio_start+4]),
++#endif
++                                      (u32)&MCF_SSI_TX1,
++                                      MCF_EDMA_TCD_ATTR_SSIZE_32BIT | MCF_EDMA_TCD_ATTR_DSIZE_32BIT,
++                                      8,
++                                      4,
++                                      0,
++                                      audio_count/8,
++                                      audio_count/8,
++                                      0,
++                                      0,
++                                      1, // major_int
++                    0  // disable_req
++                                      );
++
++      audio_device->dmaing = 1;
++      audio_txbusy = 1;
++
++      start_edma_transfer(DMA_TCD);
++      start_edma_transfer(DMA_TCD2);
++#if 0
++      MCF_EDMA_ERQ |= (1<<DMA_TCD) | (1<<DMA_TCD2);
++      MCF_EDMA_SSRT = DMA_TCD;
++      MCF_EDMA_SSRT = DMA_TCD2;
++#endif
++
++}
++
++/*
++ *    Start DMA'ing a new buffer of data if any available.
++ */
++static void ssi_audio_dmabuf(void)
++{
++#ifdef AUDIO_DEBUG
++      printk(DRIVER_NAME ":ssi_audio_dmabuf(): append=%x start=%x\n", audio_append, audio_appstart);
++#endif
++
++      /* If already running then nothing to do... */
++      if (audio_device->dmaing)
++              return;
++
++      /* Set DMA buffer size */
++      audio_count = (audio_append >= audio_appstart) ?
++              (audio_append - audio_appstart) :
++              (BUFSIZE - audio_appstart);
++      if (audio_count > DMASIZE)
++              audio_count = DMASIZE;
++
++      /* Adjust pointers and counters accordingly */
++      audio_appstart += audio_count;
++      if (audio_appstart >= BUFSIZE)
++              audio_appstart = 0;
++
++      if (audio_count > 0)
++              ssi_audio_dmarun();
++      else {
++              audio_txbusy = 0;
++#ifdef AUDIO_DEBUG
++              printk(DRIVER_NAME ":DMA buffer is empty!\n");
++#endif
++      }
++}
++
++void __inline__ stop_dma(void) {
++      stop_edma_transfer(DMA_TCD);
++      stop_edma_transfer(DMA_TCD2);
++}
++
++static int ssi_audio_dma_handler_empty(int channel, void *dev_id)
++{
++      return IRQ_HANDLED;
++}
++
++static int ssi_audio_dma_handler(int channel, void *dev_id)
++{
++#ifdef AUDIO_DEBUG
++      printk(DRIVER_NAME ":ssi_audio_dma_handler(channel=%d)\n", channel);
++#endif
++
++      /* Clear DMA interrupt */
++      stop_dma();
++
++      audio_device->dmaing = 0;
++
++      /* Update data pointers and counts */
++      audio_start += audio_count;
++      if (audio_start >= BUFSIZE)
++              audio_start = 0;
++      audio_count = 0;
++
++      /* Start new DMA buffer if we can */
++      ssi_audio_dmabuf();
++
++      return IRQ_HANDLED;
++}
++
++static void init_dma(void)
++{
++      /* SSI DMA Signals mapped to DMA request */
++      MCF_CCM_MISCCR &= ~MCF_CCM_MISCCR_TIMDMA;
++      init_edma();
++}
++
++#endif        /* CONFIG_SSIAUDIO_USE_EDMA */
++
++
++/* Write CODEC register using SPI
++ *   address - CODEC register address
++ *   data - data to be written into register
++ */
++static int codec_write(u8 addr, u16 data)
++{
++      u16 spi_word;
++
++      if (audio_device==NULL || audio_device->spi==NULL)
++              return -ENODEV;
++
++      spi_word = ((addr & 0x7F)<<9)|(data & 0x1FF);
++      return spi_write(audio_device->spi, (const u8*)&spi_word, sizeof(spi_word));
++}
++
++static inline void enable_ssi(void)
++{
++      if (audio_device==NULL || audio_device->ssi_enabled) return;
++      audio_device->ssi_enabled = 1;
++      MCF_SSI_CR |= MCF_SSI_CR_SSI_EN;  /* enable SSI module */
++      MCF_SSI_CR |= MCF_SSI_CR_TE;      /* enable tranmitter */
++}
++
++static inline void disable_ssi(void)
++{
++      if (audio_device==NULL || audio_device->ssi_enabled==0) return;
++      MCF_SSI_CR &= ~MCF_SSI_CR_TE;           /* disable transmitter */
++      MCF_SSI_CR &= ~MCF_SSI_CR_SSI_EN;       /* disable SSI module  */
++      audio_device->ssi_enabled = 0;
++}
++
++/* Audio CODEC initialization */
++/* TODO: also the SSI frequency/dividers must be adjusted */
++static void adjust_codec_speed(void) {
++#ifdef AUDIO_DEBUG
++      printk(DRIVER_NAME ":adjust_codec_speed: %d\n", audio_device->speed);
++#endif
++
++      if (audio_device->speed == 8000) {
++              codec_write(CODEC_SAMPLE_RATE_REG,CODEC_SAMPLE_8KHZ);
++      } else if (audio_device->speed == 16000) {
++              codec_write(CODEC_SAMPLE_RATE_REG,CODEC_SAMPLE_16KHZ);
++      } else if (audio_device->speed == 22000) {
++              codec_write(CODEC_SAMPLE_RATE_REG,CODEC_SAMPLE_22KHZ);
++      } else if (audio_device->speed == 44000 || audio_device->speed == 44100) {
++              codec_write(CODEC_SAMPLE_RATE_REG,CODEC_SAMPLE_44KHZ);
++      } else if (audio_device->speed == 48000) {
++              codec_write(CODEC_SAMPLE_RATE_REG,CODEC_SAMPLE_48KHZ);
++      } else {
++              /* default 44KHz */
++              codec_write(CODEC_SAMPLE_RATE_REG,CODEC_SAMPLE_44KHZ);
++      }
++}
++
++static void codec_reset(void)
++{
++      codec_write(CODEC_RESET_REG, 0); /* reset the audio chip */
++      udelay(1500); /* wait for reset */
++}
++
++static void init_audio_codec(void)
++{
++#ifdef AUDIO_DEBUG
++      printk(DRIVER_NAME ":init_audio_codec()\n");
++#endif
++      codec_reset();
++
++      codec_write(CODEC_LEFT_IN_REG, 0x017);
++      codec_write(CODEC_RIGHT_IN_REG, 0x017);
++      codec_write(CODEC_POWER_DOWN_REG, 0x000); /* Turn off line input */
++      codec_write(CODEC_DIGITAL_IF_FMT_REG, 0x00A); /* I2S slave mode */
++      /* codec_write(CODEC_DIGITAL_IF_FMT_REG, 0x042); // I2S master mode */
++      codec_write(CODEC_DIGITAL_APATH_REG, 0x007); /* Set A path */
++
++      /* set sample rate */
++    adjust_codec_speed();
++
++      codec_write(CODEC_LEFT_HP_VOL_REG, 0x075); /* set volume */
++      codec_write(CODEC_RIGHT_HP_VOL_REG, 0x075); /* set volume */
++      codec_write(CODEC_DIGITAL_IF_ACT_REG, 1); /* Activate digital interface */
++      codec_write(CODEC_ANALOG_APATH_REG, 0x0F2);
++}
++
++
++static void chip_init(void)
++{
++#ifdef CONFIG_SSIAUDIO_USE_EDMA
++      init_dma();
++#endif
++
++      /* Enable the SSI pins */
++      MCF_GPIO_PAR_SSI = ( 0
++                                                      | MCF_GPIO_PAR_SSI_MCLK
++                                                      | MCF_GPIO_PAR_SSI_STXD(3)
++                                                      | MCF_GPIO_PAR_SSI_SRXD(3)
++                                                      | MCF_GPIO_PAR_SSI_FS(3)
++                                                      | MCF_GPIO_PAR_SSI_BCLK(3) );
++
++}
++
++static void init_ssi(void)
++{
++#ifdef AUDIO_DEBUG
++      printk(DRIVER_NAME ":init_ssi()\n");
++#endif
++
++      /* Dividers are for MCF54445 on 266Mhz, the output is 44.1Khz*/
++      /* Enable SSI clock in CCM */
++      MCF_CCM_CDR = MCF_CCM_CDR_SSIDIV(47);
++
++      /* Issue a SSI reset */
++      MCF_SSI_CR &= ~MCF_SSI_CR_SSI_EN;  /* disable SSI module */
++
++      /* SSI module uses internal CPU clock */
++      MCF_CCM_MISCCR |= MCF_CCM_MISCCR_SSISRC;
++
++      MCF_CCM_MISCCR |= MCF_CCM_MISCCR_SSIPUE;
++      MCF_CCM_MISCCR |= MCF_CCM_MISCCR_SSIPUS_UP;
++
++      MCF_SSI_CR = 0
++                      | MCF_SSI_CR_CIS
++                      | MCF_SSI_CR_TCH    /* Enable two channel mode */
++                      | MCF_SSI_CR_MCE    /* Set clock out on SSI_MCLK pin */
++                      | MCF_SSI_CR_I2S_MASTER    /* Set I2S master mode */
++                      | MCF_SSI_CR_SYN      /* Enable synchronous mode */
++                      | MCF_SSI_CR_NET
++                      ;
++
++      MCF_SSI_TCR = 0
++                      | MCF_SSI_TCR_TXDIR  /* internally generated bit clock */
++                      | MCF_SSI_TCR_TFDIR  /* internally generated frame sync */
++                      | MCF_SSI_TCR_TSCKP  /* Clock data on falling edge of bit clock */
++                      | MCF_SSI_TCR_TFSI   /* Frame sync active low */
++                      | MCF_SSI_TCR_TEFS   /* TX frame sync 1 bit before data */
++                      | MCF_SSI_TCR_TFEN0  /* TX FIFO 0 enabled */
++                      | MCF_SSI_TCR_TFEN1  /* TX FIFO 1 enabled */
++                      | MCF_SSI_TCR_TXBIT0
++                      ;
++
++      MCF_SSI_CCR = MCF_SSI_CCR_WL(7)  /* 16 bit word length */
++                      | MCF_SSI_CCR_DC(1)      /* Frame rate divider */
++                      | MCF_SSI_CCR_PM(0)
++                      | MCF_SSI_CCR_DIV2
++                      ;
++
++      MCF_SSI_FCSR = 0
++                      | MCF_SSI_FCSR_TFWM0(0)
++                      | MCF_SSI_FCSR_TFWM1(0)
++                      ;
++
++      MCF_SSI_IER =   0 // interrupts
++#ifndef CONFIG_SSIAUDIO_USE_EDMA
++                              | MCF_SSI_IER_TIE   /* transmit interrupts */
++                              | MCF_SSI_IER_TFE0  /* transmit FIFO 0 empty */
++                              | MCF_SSI_IER_TFE1  /* transmit FIFO 1 empty */
++#else
++                              | MCF_SSI_IER_TDMAE /* DMA request enabled */
++                              | MCF_SSI_IER_TFE0  /* transmit FIFO 0 empty */
++                              | MCF_SSI_IER_TFE1  /* transmit FIFO 1 empty */
++#endif
++                              ;
++
++#ifndef CONFIG_SSIAUDIO_USE_EDMA
++      /* enable IRQ:  SSI interrupt */
++      MCF_INTC1_ICR(M5445x_AUDIO_IRQ_SOURCE) = M5445x_AUDIO_IRQ_LEVEL;
++      MCF_INTC1_CIMR = M5445x_AUDIO_IRQ_SOURCE;
++#endif
++}
++
++#ifndef CONFIG_SSIAUDIO_USE_EDMA
++/* interrupt for SSI */
++static int ssi_audio_isr(int irq, void *dev_id)
++{
++      unsigned long   *bp;
++
++      if (audio_txbusy==0) {
++              return IRQ_HANDLED;
++      }
++
++      spin_lock(&(audio_device->lock));
++
++      if (audio_start == audio_append) {
++              disable_ssi();
++              audio_txbusy = 0;
++      } else {
++              if (MCF_SSI_ISR & (MCF_SSI_ISR_TFE0|MCF_SSI_ISR_TFE1)) {
++                      bp = (unsigned long *) &audio_device->audio_buf[audio_start];
++                      if (audio_device->channel) {
++                              MCF_SSI_TX1 = *bp;
++                              audio_device->channel = 0;
++                      } else {
++                              MCF_SSI_TX0 = *bp;
++                              audio_device->channel = 1;
++                      }
++                      audio_start += 4;
++                      if (audio_start >= BUFSIZE)
++                              audio_start = 0;
++              }
++      }
++
++      spin_unlock(&(audio_device->lock));
++
++      return IRQ_HANDLED;
++}
++#endif
++
++/* Set initial driver playback defaults. */
++static void init_driver_variables(void)
++{
++      audio_device->speed = 44100;
++      audio_device->format = AFMT_S16_LE;
++      audio_device->bits = 16;
++      audio_device->stereo = 1;
++      audio_device->ssi_enabled = 0;
++
++      audio_start = 0;
++      audio_count = 0;
++      audio_append = 0;
++      audio_appstart = 0;
++      audio_txbusy = 0;
++      audio_device->dmaing = 0;
++}
++
++/* open audio device */
++static int ssi_audio_open(struct inode *inode, struct file *filp)
++{
++#ifdef AUDIO_DEBUG
++      printk(DRIVER_NAME ":ssi_audio_open()\n");
++#endif
++
++      if (audio_device==NULL) return (-ENODEV);
++
++      if (audio_device->isopen)
++              return(-EBUSY);
++
++      spin_lock(&(audio_device->lock));
++
++      audio_device->isopen = 1;
++
++      init_driver_variables();
++      init_ssi();
++      init_audio_codec();
++
++      spin_unlock(&(audio_device->lock));
++
++      udelay(100);
++
++      return 0;
++}
++
++/* close audio device */
++static int ssi_audio_close(struct inode *inode, struct file *filp)
++{
++#ifdef AUDIO_DEBUG
++      printk(DRIVER_NAME ":ssi_audio_close()\n");
++#endif
++
++      if (audio_device==NULL) return (-ENODEV);
++
++      ssi_audio_txdrain();
++
++      spin_lock(&(audio_device->lock));
++
++#ifdef CONFIG_SSIAUDIO_USE_EDMA
++      stop_dma();
++#endif
++      disable_ssi();
++      codec_reset();
++      init_driver_variables();
++      audio_device->isopen = 0;
++
++      spin_unlock(&(audio_device->lock));
++      return 0;
++}
++
++/* write to audio device */
++static ssize_t ssi_audio_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
++{
++      unsigned long   *dp, *buflp;
++      unsigned short  *bufwp;
++      unsigned char   *bufbp;
++      unsigned int    slen, bufcnt, i, s, e;
++
++#ifdef AUDIO_DEBUG
++      printk(DRIVER_NAME ":ssi_audio_write(buf=%x,count=%d)\n", (int) buf, count);
++#endif
++
++      if (audio_device==NULL) return (-ENODEV);
++
++      if (count <= 0)
++              return 0;
++
++      spin_lock(&(audio_device->lock));
++
++      buflp = (unsigned long *) buf;
++      bufwp = (unsigned short *) buf;
++      bufbp = (unsigned char *) buf;
++
++      bufcnt = count & ~0x3;
++
++      bufcnt <<= 1;
++      if (audio_device->stereo == 0)
++              bufcnt <<= 1;
++      if (audio_device->bits == 8)
++              bufcnt <<= 1;
++
++tryagain:
++      /*
++       *      Get a snapshot of buffer, so we can figure out how
++       *      much data we can fit in...
++       */
++      s = audio_start;
++      e = audio_append;
++      dp = (unsigned long *) &(audio_device->audio_buf[e]);
++
++      slen = ((s > e) ? (s - e) : (BUFSIZE - (e - s))) - 4;
++      if (slen > bufcnt)
++              slen = bufcnt;
++      if ((BUFSIZE - e) < slen)
++              slen = BUFSIZE - e;
++
++      if (slen == 0) {
++              if (signal_pending(current))
++                      return(-ERESTARTSYS);
++              set_current_state(TASK_INTERRUPTIBLE);
++              schedule_timeout(1);
++              goto tryagain;
++      }
++
++      /* For DMA we need to have data as 32 bit
++         values (since SSI TX register is 32 bit).
++         So, the incomming 16 bit data must be put to buffer as 32 bit values.
++         Also, the endianess is converted if needed
++      */
++      if (audio_device->stereo) {
++              if (audio_device->bits == 16) {
++                      if (audio_device->format==AFMT_S16_LE) {
++                              /*- convert endianess, probably could be done by SSI also */
++                              for (i = 0; (i < slen); i += 4) {
++                                      unsigned short val = le16_to_cpu((*bufwp++));
++                                      *dp++ = val;
++                              }
++                      } else {
++                              for (i = 0; (i < slen); i += 4) {
++                                      *dp++ = *bufwp++;
++                              }
++                      }
++              } else {
++                      for (i = 0; (i < slen); i += 4) {
++                              *dp    = (((unsigned long) *bufbp++) << 24);
++                              *dp++ |= (((unsigned long) *bufbp++) << 8);
++                      }
++              }
++      } else {
++              if (audio_device->bits == 16) {
++                      for (i = 0; (i < slen); i += 4) {
++                              *dp++ = (((unsigned long)*bufwp)<<16) | *bufwp;
++                              bufwp++;
++                      }
++              } else {
++                      for (i = 0; (i < slen); i += 4) {
++                              *dp++ = (((unsigned long) *bufbp) << 24) |
++                                      (((unsigned long) *bufbp) << 8);
++                              bufbp++;
++                      }
++              }
++      }
++
++      e += slen;
++      if (e >= BUFSIZE)
++              e = 0;
++      audio_append = e;
++
++      /* If not outputing audio, then start now */
++      if (audio_txbusy == 0) {
++              audio_txbusy++;
++              audio_device->channel = 0;
++              enable_ssi();
++#ifdef CONFIG_SSIAUDIO_USE_EDMA
++              ssi_audio_dmabuf(); /* start first DMA transfer */
++#endif
++      }
++
++      bufcnt -= slen;
++
++      if (bufcnt > 0)
++              goto tryagain;
++
++      spin_unlock(&(audio_device->lock));
++
++      return count;
++}
++
++/* ioctl: control the driver */
++static int ssi_audio_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
++{
++      long    val;
++      int     rc = 0;
++
++#ifdef AUDIO_DEBUG
++    printk(DRIVER_NAME ":ssi_audio_ioctl(cmd=%x,arg=%x)\n", (int) cmd, (int) arg);
++#endif
++
++      if (audio_device==NULL) return (-ENODEV);
++
++      switch (cmd) {
++
++      case SNDCTL_DSP_SPEED:
++              if (access_ok(VERIFY_READ, (void *) arg, sizeof(val))) {
++                      get_user(val, (unsigned long *) arg);
++#ifdef AUDIO_DEBUG
++                      printk(DRIVER_NAME ":ssi_audio_ioctl: SNDCTL_DSP_SPEED: %ld\n", val);
++#endif
++                      ssi_audio_txdrain();
++                      audio_device->speed = val;
++                      init_audio_codec();
++              } else {
++                      rc = -EINVAL;
++              }
++              break;
++
++      case SNDCTL_DSP_SAMPLESIZE:
++              if (access_ok(VERIFY_READ, (void *) arg, sizeof(val))) {
++                      get_user(val, (unsigned long *) arg);
++                      ssi_audio_txdrain();
++                      ssi_audio_setsamplesize(val);
++              } else {
++                      rc = -EINVAL;
++              }
++              break;
++
++      case SNDCTL_DSP_STEREO:
++              if (access_ok(VERIFY_READ, (void *) arg, sizeof(val))) {
++                      get_user(val, (unsigned long *) arg);
++                      ssi_audio_txdrain();
++                      audio_device->stereo = val;
++              } else {
++                      rc = -EINVAL;
++              }
++              break;
++
++      case SNDCTL_DSP_GETBLKSIZE:
++              if (access_ok(VERIFY_WRITE, (void *) arg, sizeof(long)))
++                      put_user(BUFSIZE, (long *) arg);
++              else
++                      rc = -EINVAL;
++              break;
++
++      case SNDCTL_DSP_SYNC:
++              ssi_audio_txdrain();
++              break;
++
++      default:
++              rc = -EINVAL;
++              break;
++      }
++
++      return rc;
++}
++
++/****************************************************************************/
++
++struct file_operations        ssi_audio_fops = {
++      open: ssi_audio_open,           /* open */
++      release: ssi_audio_close,       /* close */
++      write: ssi_audio_write,         /* write */
++      ioctl: ssi_audio_ioctl,         /* ioctl */
++};
++
++/* initialize audio driver */
++static int __devinit ssi_audio_probe(struct spi_device *spi)
++{
++      struct ssi_audio        *audio;
++      int     err;
++
++#ifdef AUDIO_DEBUG
++      printk(DRIVER_NAME": probe\n");
++#endif
++
++      if (!spi->irq) {
++              dev_dbg(&spi->dev, "no IRQ?\n");
++              return -ENODEV;
++      }
++
++      /* don't exceed max specified sample rate */
++      if (spi->max_speed_hz > MAX_SPEED_HZ) {
++              dev_dbg(&spi->dev, "f(sample) %d KHz?\n",
++                              (spi->max_speed_hz)/1000);
++              return -EINVAL;
++      }
++
++      /* register charcter device */
++      if (register_chrdev(SOUND_MAJOR, SOUND_DEVICE_NAME, &ssi_audio_fops) < 0) {
++              printk(KERN_WARNING DRIVER_NAME ": failed to register major %d\n", SOUND_MAJOR);
++              dev_dbg(&spi->dev, DRIVER_NAME ": failed to register major %d\n", SOUND_MAJOR);
++              return -ENODEV;
++      }
++
++      audio = kzalloc(sizeof(struct ssi_audio), GFP_KERNEL);
++      if (!audio) {
++              err = -ENOMEM;
++              goto err_out;
++      }
++
++      /* DMA buffer must be from GFP_DMA zone, so it will not be cached */
++      audio->audio_buf = kmalloc(BUFSIZE, GFP_DMA);
++      if (audio->audio_buf == NULL) {
++              dev_dbg(&spi->dev, DRIVER_NAME ": failed to allocate DMA[%d] buffer\n", BUFSIZE);
++              err = -ENOMEM;
++              goto err_free_mem;
++      }
++
++      audio_device = audio;
++
++      dev_set_drvdata(&spi->dev, audio);
++      spi->dev.power.power_state = PMSG_ON;
++
++      audio->spi = spi;
++
++#ifndef CONFIG_SSIAUDIO_USE_EDMA
++      if (request_irq(spi->irq, ssi_audio_isr, SA_INTERRUPT,  spi->dev.bus_id, audio)) {
++              dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
++              err = -EBUSY;
++              goto err_free_mem;
++      }
++
++#else
++      /* request 2 eDMA channels since two channel output mode is used */
++      if (request_edma_channel(DMA_TCD,
++                                                      ssi_audio_dma_handler_empty,
++                                                      NULL,
++                                                      audio,
++                                                      &(audio_device->lock),
++                                                      DRIVER_NAME
++                                                      )!=0)
++      {
++              dev_dbg(&spi->dev, "DMA channel %d busy?\n", DMA_TCD);
++              err = -EBUSY;
++              goto err_free_mem;
++      }
++      if (request_edma_channel(DMA_TCD2,
++                                                      ssi_audio_dma_handler,
++                                                      NULL,
++                                                      audio,
++                                                      &(audio_device->lock),
++                                                      DRIVER_NAME
++                                                      )!=0)
++      {
++              dev_dbg(&spi->dev, "DMA channel %d busy?\n", DMA_TCD2);
++              err = -EBUSY;
++              goto err_free_mem;
++      }
++
++#endif
++      chip_init();
++      printk(DRIVER_NAME ": Probed successfully\n");
++
++      return 0;
++
++ err_free_mem:
++      kfree(audio);
++      audio_device = NULL;
++ err_out:
++      unregister_chrdev(SOUND_MAJOR, SOUND_DEVICE_NAME);
++      return err;
++}
++
++static int __devexit ssi_audio_remove(struct spi_device *spi)
++{
++      struct ssi_audio *audio = dev_get_drvdata(&spi->dev);
++
++      ssi_audio_txdrain();
++#ifndef CONFIG_SSIAUDIO_USE_EDMA
++      free_irq(spi->irq, audio);
++#else
++      free_edma_channel(DMA_TCD, audio);
++      free_edma_channel(DMA_TCD2, audio);
++#endif
++      kfree(audio->audio_buf);
++      kfree(audio);
++      audio_device = NULL;
++      unregister_chrdev(SOUND_MAJOR, SOUND_DEVICE_NAME);
++      dev_dbg(&spi->dev, "unregistered audio\n");
++      return 0;
++}
++
++static int ssi_audio_suspend(struct spi_device *spi, pm_message_t message) {
++      return 0;
++}
++
++static int ssi_audio_resume(struct spi_device *spi) {
++      return 0;
++}
++
++static struct spi_driver ssi_audio_driver = {
++      .driver = {
++              .name   = DRIVER_NAME,
++              .bus    = &spi_bus_type,
++              .owner  = THIS_MODULE,
++      },
++      .probe          = ssi_audio_probe,
++      .remove         = __devexit_p(ssi_audio_remove),
++      .suspend        = ssi_audio_suspend,
++      .resume         = ssi_audio_resume,
++};
++
++static int __init ssi_audio_init(void)
++{
++      return spi_register_driver(&ssi_audio_driver);
++}
++module_init(ssi_audio_init);
++
++static void __exit ssi_audio_exit(void)
++{
++      spi_unregister_driver(&ssi_audio_driver);
++}
++module_exit(ssi_audio_exit);
++
++MODULE_DESCRIPTION("SSI/I2S Audio Driver");
++MODULE_LICENSE("GPL");
+--- a/include/asm-m68k/coldfire_edma.h
++++ b/include/asm-m68k/coldfire_edma.h
+@@ -1,39 +1,102 @@
++/*
++ * coldfire_edma.h - eDMA driver for Coldfire MCF5445x
++ *
++ * Yaroslav Vinogradov yaroslav.vinogradov@freescale.com
++ *
++ * Copyright Freescale Semiconductor, Inc. 2007
++ *
++ * 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.
++ */
++
+ #ifndef _LINUX_COLDFIRE_DMA_H
+ #define _LINUX_COLDFIRE_DMA_H
+ #include <linux/interrupt.h>
++#include <asm/mcf5445x_edma.h>
+-#define EDMA_DRIVER_NAME              "ColdFire-eDMA"
+-#define DMA_DEV_MINOR                         1
++#define EDMA_DRIVER_NAME "ColdFire-eDMA"
++#define DMA_DEV_MINOR 1
+ #define EDMA_INT_CHANNEL_BASE                 8
+ #define EDMA_INT_CONTROLLER_BASE      64
+ #define EDMA_CHANNELS                 16
+-
++ 
+ #define EDMA_IRQ_LEVEL                        5
+-
++ 
+ typedef irqreturn_t (*edma_irq_handler)(int, void *);
+ typedef void (*edma_error_handler)(int, void *);
+-
++ 
++/* Setup transfer control descriptor (TCD)
++ *   channel - descriptor number
++ *   source  - source address
++ *   dest    - destination address
++ *   attr    - attributes
++ *   soff    - source offset
++ *   nbytes  - number of bytes to be transfered in minor loop
++ *   slast   - last source address adjustment
++ *   citer   - major loop count
++ *   biter   - beggining minor loop count
++ *   doff    - destination offset
++ *   dlast_sga - last destination address adjustment
++ *   major_int - generate interrupt after each major loop
++ *   disable_req - disable DMA request after major loop
++ */
+ void set_edma_params(int channel, u32 source, u32 dest,
+-      u32 attr, u32 soff, u32 nbytes, u32 slast,
+-      u32 citer, u32 biter, u32 doff, u32 dlast_sga);
+-
+-void start_edma_transfer(int channel, int major_int);
+-
+-void stop_edma_transfer(int channel);
+-
+-void confirm_edma_interrupt_handled(int channel);
+-
++              u32 attr, u32 soff, u32 nbytes, u32 slast,
++              u32 citer, u32 biter, u32 doff, u32 dlast_sga,
++              int major_int, int disable_req);
++
++/* Starts eDMA transfer on specified channel
++ *   channel - eDMA TCD number
++ */
++static inline void  start_edma_transfer(int channel)
++{
++      MCF_EDMA_SERQ = channel;
++      MCF_EDMA_SSRT = channel;
++}
++
++/* Stops eDMA transfer
++ *   channel - eDMA TCD number
++ */
++static inline void stop_edma_transfer(int channel)
++{
++      MCF_EDMA_CINT = channel;
++      MCF_EDMA_CERQ = channel;
++}
++
++
++/* Confirm that interrupt has been handled
++ *   channel - eDMA TCD number
++ */
++static inline void confirm_edma_interrupt_handled(int channel)
++{
++      MCF_EDMA_CINT = channel;
++}
++ 
++/* Initialize eDMA controller */
+ void init_edma(void);
+-
+-int  request_edma_channel(int channel,
+-                      edma_irq_handler handler,
+-                      edma_error_handler error_handler,
+-                      void *dev,
+-                      spinlock_t *lock,
+-                      const char *device_id);
+-
++ 
++/* Request eDMA channel:
++ *   channel - eDMA TCD number
++ *   handler - channel IRQ callback
++ *   error_handler - error interrupt handler callback for channel
++ *   dev - device
++ *   lock - spinlock to be locked (can be NULL)
++ *   device_id - device driver name for proc file system output
++ */
++int request_edma_channel(int channel,
++              edma_irq_handler handler,
++              edma_error_handler error_handler,
++              void *dev,
++              spinlock_t *lock,
++              const char *device_id);
++  
++/* Free eDMA channel
++ *  channel - eDMA TCD number
++ *  dev - device
++ */
+ int free_edma_channel(int channel, void *dev);
+-
+ #endif
+--- /dev/null
++++ b/include/linux/spi/mcfqspi.h
+@@ -0,0 +1,80 @@
++/****************************************************************************/
++
++/*
++ *    mcfqspi.c - Master QSPI controller for the ColdFire processors
++ *
++ *    (C) Copyright 2005, Intec Automation,
++ *                        Mike Lavender (mike@steroidmicros)
++ *
++
++     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.
++
++     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.                     */
++/* ------------------------------------------------------------------------- */
++
++#ifndef MCFQSPI_H_
++#define MCFQSPI_H_
++
++#define QSPI_CS_INIT          0x01
++#define QSPI_CS_ASSERT                0x02
++#define QSPI_CS_DROP          0x04
++
++#define QSPIIOCS_DOUT_HIZ     1       /* QMR[DOHIE] set hi-z dout between transfers */
++#define QSPIIOCS_BITS         2       /* QMR[BITS] set transfer size */
++#define QSPIIOCG_BITS         3       /* QMR[BITS] get transfer size */
++#define QSPIIOCS_CPOL         4       /* QMR[CPOL] set SCK inactive state */
++#define QSPIIOCS_CPHA         5       /* QMR[CPHA] set SCK phase, 1=rising edge */
++#define QSPIIOCS_BAUD         6       /* QMR[BAUD] set SCK baud rate divider */
++#define QSPIIOCS_QCD          7       /* QDLYR[QCD] set start delay */
++#define QSPIIOCS_DTL          8       /* QDLYR[DTL] set after delay */
++#define QSPIIOCS_CONT         9       /* continuous CS asserted during transfer */
++#define QSPIIOCS_READDATA     10      /* set data send during read */
++#define QSPIIOCS_ODD_MOD      11      /* if length of buffer is a odd number, 16-bit transfers */
++                                      /* are finalized with a 8-bit transfer */
++#define QSPIIOCS_DSP_MOD      12      /* transfers are bounded to 15/30 bytes (a multiple of 3 bytes = 1 DSPword) */
++#define QSPIIOCS_POLL_MOD     13      /* driver uses polling instead of interrupts */
++
++#define QSPIIOCS_SET_CSIV     14      /* sets CSIV flag (cs inactive level) */
++
++#ifdef CONFIG_M520x
++#undef  MCF_GPIO_PAR_QSPI
++#define MCF_GPIO_PAR_QSPI      (0xA4034)
++#endif
++
++struct coldfire_spi_master {
++      u16 bus_num;
++      u16 num_chipselect;
++      u8  irq_source;
++      u32 irq_vector;
++      u32 irq_mask;
++      u8  irq_lp;
++      u8  par_val;
++      u16 par_val16;
++      void (*cs_control)(u8 cs, u8 command);
++};
++
++
++struct coldfire_spi_chip {
++      u8 mode;
++      u8 bits_per_word;
++      u8 del_cs_to_clk;
++      u8 del_after_trans;
++      u16 void_write_data;
++};
++
++typedef struct qspi_read_data {
++        __u32 length;
++        __u8 *buf;                   /* data to send during read */
++        unsigned int loop : 1;
++} qspi_read_data;
++#endif /*MCFQSPI_H_*/