aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/coldfire/patches/019-m5445x_spi.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/coldfire/patches/019-m5445x_spi.patch')
-rw-r--r--target/linux/coldfire/patches/019-m5445x_spi.patch3345
1 files changed, 3345 insertions, 0 deletions
diff --git a/target/linux/coldfire/patches/019-m5445x_spi.patch b/target/linux/coldfire/patches/019-m5445x_spi.patch
new file mode 100644
index 0000000000..49b83e0b18
--- /dev/null
+++ b/target/linux/coldfire/patches/019-m5445x_spi.patch
@@ -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_*/