diff options
author | Imre Kaloz <kaloz@openwrt.org> | 2010-11-22 13:31:46 +0000 |
---|---|---|
committer | Imre Kaloz <kaloz@openwrt.org> | 2010-11-22 13:31:46 +0000 |
commit | d6ea3e24d49f94051305b5191edd10d213f92bf3 (patch) | |
tree | d92ba23cf07ca211cd6100bb144831295b2bea54 /target/linux/coldfire/patches/019-m5445x_spi.patch | |
parent | d066a4c712aae3cd1aaa5108f7cd2cae81c6343b (diff) | |
download | master-31e0f0ae-d6ea3e24d49f94051305b5191edd10d213f92bf3.tar.gz master-31e0f0ae-d6ea3e24d49f94051305b5191edd10d213f92bf3.tar.bz2 master-31e0f0ae-d6ea3e24d49f94051305b5191edd10d213f92bf3.zip |
remove 2.6.25 support
SVN-Revision: 24088
Diffstat (limited to 'target/linux/coldfire/patches/019-m5445x_spi.patch')
-rw-r--r-- | target/linux/coldfire/patches/019-m5445x_spi.patch | 3345 |
1 files changed, 0 insertions, 3345 deletions
diff --git a/target/linux/coldfire/patches/019-m5445x_spi.patch b/target/linux/coldfire/patches/019-m5445x_spi.patch deleted file mode 100644 index 49b83e0b18..0000000000 --- a/target/linux/coldfire/patches/019-m5445x_spi.patch +++ /dev/null @@ -1,3345 +0,0 @@ -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_*/ |