Index: linux-2.6.30.9/drivers/spi/Kconfig
===================================================================
--- linux-2.6.30.9.orig/drivers/spi/Kconfig	2009-11-24 21:09:23.000000000 +0100
+++ linux-2.6.30.9/drivers/spi/Kconfig	2009-11-24 21:09:53.000000000 +0100
@@ -125,6 +125,12 @@
 
 	  If unsure, say N.
 
+config SPI_EP93XX
+	tristate "EP93xx SSP SPI master"
+	depends on SPI_MASTER && ARCH_EP93XX && EXPERIMENTAL
+	help
+	  This enables the EP93xx SPI master controller.
+
 config SPI_IMX
 	tristate "Freescale iMX SPI controller"
 	depends on ARCH_IMX && EXPERIMENTAL
Index: linux-2.6.30.9/drivers/spi/Makefile
===================================================================
--- linux-2.6.30.9.orig/drivers/spi/Makefile	2009-11-24 21:09:23.000000000 +0100
+++ linux-2.6.30.9/drivers/spi/Makefile	2009-11-24 21:09:53.000000000 +0100
@@ -16,6 +16,7 @@
 obj-$(CONFIG_SPI_BITBANG)		+= spi_bitbang.o
 obj-$(CONFIG_SPI_AU1550)		+= au1550_spi.o
 obj-$(CONFIG_SPI_BUTTERFLY)		+= spi_butterfly.o
+obj-$(CONFIG_SPI_EP93XX)		+= spi_ep93xx.o
 obj-$(CONFIG_SPI_GPIO)			+= spi_gpio.o
 obj-$(CONFIG_SPI_GPIO_OLD)		+= spi_gpio_old.o
 obj-$(CONFIG_SPI_IMX)			+= spi_imx.o
Index: linux-2.6.30.9/drivers/spi/spi_ep93xx.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.30.9/drivers/spi/spi_ep93xx.c	2009-11-24 21:21:25.000000000 +0100
@@ -0,0 +1,680 @@
+/*
+ * linux/drivers/spi/spi_ep93xx.c
+ *
+ * Copyright (C) 2007 Manfred Gruber <m.gruber@tirol.com>
+ * Small changes by Peter Ivanov <ivanovp@gmail.com> to support MMC over SPI, 2008
+ * SIM.ONE changes by Nuccio Raciti Simplemachine <nuccio.raciti@gmail.com>
+ *
+ * Based on pxa2xx_spi.c/spi_imx.c and bitbang.c driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#include <linux/spi/spi.h>
+
+#include <mach/hardware.h>
+#include <mach/ep93xx-regs.h>
+#include <asm/gpio.h>
+
+/* #define SPI_EP93XX_DEBUG */
+
+#define DEFINE_SSP_REG(reg, off)			\
+	static inline u32 read_##reg(void *p)		\
+		{ return __raw_readl(p + (off)); }	\
+	static inline void write_##reg(u32 v, void *p)	\
+		{ __raw_writel(v, p + (off)); }
+
+DEFINE_SSP_REG(SSPCR0, 0x00)
+DEFINE_SSP_REG(SSPCR1, 0x04)
+DEFINE_SSP_REG(SSPDR, 0x08)
+DEFINE_SSP_REG(SSPSR, 0x0c)
+DEFINE_SSP_REG(SSPCPSR, 0x10)
+DEFINE_SSP_REG(SSPIIR, 0x14)
+DEFINE_SSP_REG(SSPICR, 0x14)
+
+/* Bits in SSPCR0 */
+#define SSPCR0_DSS_MASK		0x0000000f
+#define SSPCR0_FRF_MASK		0x00000030
+#define SSPCR0_FRF_SHIFT	4
+#define SSPCR0_FRF_MOTOROLA	(0 << SSPCR0_FRF_SHIFT)
+#define SSPCR0_FRF_TI		(1 << SSPCR0_FRF_SHIFT)
+#define SSPCR0_FRF_NI		(2 << SSPCR0_FRF_SHIFT)
+#define SSPCR0_SPO		0x00000040
+#define SSPCR0_SPH		0x00000080
+#define SSPCR0_SCR_MASK		0x0000ff00
+#define SSPCR0_SCR_SHIFT	8
+
+/* Bits in SSPCR1 */
+#define SSPC1_RIE		0x00000001
+#define SSPC1_TIE		0x00000002
+#define SSPC1_RORIE		0x00000004
+#define SSPC1_LBM		0x00000008
+#define SSPC1_SSE		0x00000010
+#define SSPC1_MS		0x00000020
+#define SSPC1_SOD		0x00000040
+
+/* Bits in SSPSR */
+#define SSPSR_TFE		0x00000001	/* TX FIFO is empty */
+#define SSPSR_TNF		0x00000002	/* TX FIFO is not full */
+#define SSPSR_RNE		0x00000004	/* RX FIFO is not empty */
+#define SSPSR_RFF		0x00000008	/* RX FIFO is full */
+#define SSPSR_BSY		0x00000010	/* SSP is busy */
+#define SSPSR_MASK		0x0000001F	/* SSP is busy */
+
+/* Bits in SSPCPSR */
+#define SSPCPSR_SCR_MASK	0x000000ff
+
+/* Bits in SSPIIR */
+#define SSPIIR_RIS		0x00000001	/* RX FIFO IRQ status */
+#define SSPIIR_TIS		0x00000002	/* TX FIFO is not full */
+#define SSPIIR_RORIS		0x00000004	/* RX FIFO is full */
+
+#define SPI_SSPCLK		7.4e6
+#define SPI_SSPCLK_REV_E2	14.8e6		/* only for chip Rev E2 */
+#define SPI_MAX_SPEED		3.7e6
+#define SPI_MAX_SPEED_REV_E2	7.4e6		/* only for chip Rev E2 */
+#define SPI_CPSDVR_DIV_MIN	2
+#define SPI_CPSDVR_DIV_MAX	254
+#define SPI_SCR_DIV_MIN		0
+#define SPI_SCR_DIV_MAX		255
+#define SPI_DATARATE_OK		0
+#define SPI_DATARATE_NOK	-1
+
+struct driver_data {
+	/* Driver model hookup */
+	struct platform_device *pdev;
+
+	/* SPI framework hookup */
+	struct spi_master *master;
+
+	/* SSP register addresses */
+	void *ioaddr;
+
+	/* SSP irq */
+	int irq;
+
+	struct list_head queue;
+
+	/* SSP spinlock */
+	spinlock_t lock;
+
+	struct workqueue_struct *workqueue;
+	struct work_struct      work;
+
+	u8 busy;
+	u8 use_dma;
+};
+
+static unsigned ep93xx_txrx_8(struct spi_device *spi, struct spi_transfer *t)
+{
+	struct driver_data *drv_data;
+	const u8 *tx = t->tx_buf;
+	u8 *rx = t->rx_buf;
+	unsigned count = t->len;
+	u8 byte;
+	int busy;
+
+	drv_data = spi_master_get_devdata(spi->master);
+
+#ifdef SPI_EP93XX_DEBUG
+	dev_info(&spi->dev,
+		"ep93xx_txrx_8: t->len %u \n", t->len);
+#endif
+
+	while (likely(count > 0)) {
+		byte = 0;
+		if (tx) {
+			byte = *tx++;
+#ifdef SPI_EP93XX_DEBUG
+			dev_info(&spi->dev,
+				"ep93xx_txrx_8: write 0x%x \n", byte);
+#endif
+		}
+
+		write_SSPDR(byte, drv_data->ioaddr);
+		busy = read_SSPSR(drv_data->ioaddr);
+		while (busy & SSPSR_BSY) {
+			cpu_relax();
+			busy = read_SSPSR(drv_data->ioaddr);
+#ifdef SPI_EP93XX_DEBUG
+			dev_info(&spi->dev,
+				"ep93xx_txrx_8: delay. SSPSR: 0x%X\n", busy);
+#endif
+		}
+		byte = read_SSPDR(drv_data->ioaddr);
+
+		if (rx) {
+			*rx++ = byte;
+#ifdef SPI_EP93XX_DEBUG
+			dev_info(&spi->dev,
+				"ep93xx_txrx_8: read 0x%x \n", byte);
+#endif
+		}
+		count -= 1;
+	}
+	return t->len - count;
+}
+
+
+static unsigned ep93xx_txrx_16(struct spi_device *spi, struct spi_transfer *t)
+{
+
+	struct driver_data *drv_data;
+	const u16 *tx = t->tx_buf;
+	u16 *rx = t->rx_buf;
+	unsigned count = t->len;
+	u16 word;
+	int busy;
+
+	drv_data = spi_master_get_devdata(spi->master);
+
+#ifdef SPI_EP93XX_DEBUG
+	dev_info(&spi->dev,
+		"ep93xx_txrx_16: t->len %u \n", t->len);
+#endif
+	while (likely(count > 0)) {
+		word = 0;
+		if (tx) {
+			word = *tx++;
+#ifdef SPI_EP93XX_DEBUG
+			dev_info(&spi->dev,
+				"ep93xx_txrx_16: write 0x%x \n", word);
+#endif
+		}
+
+		write_SSPDR(word, drv_data->ioaddr);
+		busy = read_SSPSR(drv_data->ioaddr);
+		while (busy & SSPSR_BSY) {
+			cpu_relax();
+			busy = read_SSPSR(drv_data->ioaddr);
+#ifdef SPI_EP93XX_DEBUG
+			dev_info(&spi->dev,
+				"ep93xx_txrx_8: delay.\n");
+#endif
+		}
+
+		word = read_SSPDR(drv_data->ioaddr);
+
+		if (rx) {
+			*rx++ = word;
+#ifdef SPI_EP93XX_DEBUG
+			dev_info(&spi->dev,
+				"ep93xx_txrx_16: read 0x%x \n", word);
+#endif
+		}
+		count -= 2;
+	}
+	return t->len - count;
+}
+
+static u32 spi_data_rate(u32 speed_hz, u32 *div_cpsdvr, u32 *div_scr,
+	struct driver_data *drv_data, struct spi_device *spi)
+{
+	unsigned int spi_sspclk = SPI_SSPCLK;
+	unsigned int bus_speed_max = SPI_MAX_SPEED;
+	unsigned int bus_hz_tmp = 0;
+	u32 div_cpsdvr_tmp;
+	u32 div_scr_tmp;
+	u32 rv = SPI_DATARATE_NOK;
+	int chip_rev;
+
+	/* Checking CHIP_ID */
+	chip_rev = (__raw_readl (EP93XX_SYSCON_CHIP_ID) >> 28) & 0xF;
+	if (chip_rev == 7)
+	{
+		/* Chip version: Rev E2 */
+		/* This device has double speed SSP clock */
+		spi_sspclk = SPI_SSPCLK_REV_E2;
+		bus_speed_max = SPI_MAX_SPEED_REV_E2;
+#ifdef SPI_EP93XX_DEBUG
+		dev_info(&spi->dev,
+				"Chip Rev E2 detected! This device has double speed SSP clock.\n");
+#endif
+	}
+
+	*div_cpsdvr = SPI_CPSDVR_DIV_MAX;
+	*div_scr = SPI_SCR_DIV_MAX;
+
+	for (div_cpsdvr_tmp = SPI_CPSDVR_DIV_MIN;
+			div_cpsdvr_tmp <= SPI_CPSDVR_DIV_MAX && rv; div_cpsdvr_tmp++) {
+		for (div_scr_tmp = SPI_SCR_DIV_MIN;
+				div_scr_tmp <= SPI_SCR_DIV_MAX && rv; div_scr_tmp++) {
+			bus_hz_tmp = spi_sspclk / (div_cpsdvr_tmp * (1 + div_scr_tmp));
+			if (bus_hz_tmp <= speed_hz && bus_hz_tmp <= bus_speed_max) {
+				*div_cpsdvr = div_cpsdvr_tmp;
+				*div_scr = div_scr_tmp;
+				rv = SPI_DATARATE_OK;
+			}
+		}
+	}
+#ifdef SPI_EP93XX_DEBUG
+	dev_info(&spi->dev,
+			"Needed SPI bus frequency: %i Hz\n", speed_hz);
+	dev_info(&spi->dev,
+			"Actual SPI bus frequency: %i Hz\n", bus_hz_tmp);
+#endif
+	return rv;
+}
+
+/* Supported modes (returns -EINVAL if not supported mode requested) */
+#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)
+
+static int ep93xx_spi_setup(struct spi_device *spi)
+{
+	struct driver_data *drv_data;
+	u16 val;
+	u32 div_scr;
+	u32 div_cpsdvr;
+	unsigned int bits = spi->bits_per_word;
+	unsigned long speed_hz = spi->max_speed_hz;
+
+	drv_data = spi_master_get_devdata(spi->master);
+
+	/* enable SSP */
+	write_SSPCR1(SSPC1_SSE, drv_data->ioaddr);
+	/* Enable SSP and loopback mode (only for testing!) */
+	/* write_SSPCR1(SSPC1_SSE | SSPC1_LBM, drv_data->ioaddr);  */
+
+	if (bits == 0)
+		bits = 8;
+	if (bits < 4 || bits > 16) {
+		dev_err(&spi->dev,
+			"setup invalid bits_per_word %u (4 to 16)\n", bits);
+		return -EINVAL;
+	} else {
+		val = read_SSPCR0(drv_data->ioaddr);
+		val = val & ~SSPCR0_DSS_MASK ;
+		val = val | (bits-1);
+		write_SSPCR0(val, drv_data->ioaddr);
+#ifdef SPI_EP93XX_DEBUG
+		dev_info (&spi->dev, "Bits per word: %i\n", bits);
+#endif
+	}
+
+	if (spi->mode & ~MODEBITS) {
+		dev_err(&spi->dev, "unsupported mode bits: %x\n",
+			spi->mode & ~MODEBITS);
+		return -EINVAL;
+	} else {
+		val = read_SSPCR0(drv_data->ioaddr);
+		val = val & ~SSPCR0_SPO;
+		val = val & ~SSPCR0_SPH;
+		if (spi->mode & SPI_CPOL)
+		{
+			val = val | SSPCR0_SPO;
+		}
+#ifdef SPI_EP93XX_DEBUG
+		dev_info (&spi->dev, "Clock polarity (CPOL): %s\n", (spi->mode & SPI_CPHA) ? "1" : "0");
+#endif
+		if (spi->mode & SPI_CPHA)
+		{
+			val = val | SSPCR0_SPH;
+		}
+#ifdef SPI_EP93XX_DEBUG
+		dev_info (&spi->dev, "Clock phase (CPHA): %s\n", (spi->mode & SPI_CPHA) ? "1" : "0");
+#endif
+		write_SSPCR0(val, drv_data->ioaddr);
+	}
+
+	if (SPI_DATARATE_OK == (spi_data_rate(speed_hz, &div_cpsdvr,
+		&div_scr, drv_data, spi))) {
+
+		val = read_SSPCPSR(drv_data->ioaddr);
+		val = val & ~SSPCPSR_SCR_MASK;
+		val = val | div_cpsdvr;
+#ifdef SPI_EP93XX_DEBUG
+		dev_info (&spi->dev, "SSPCPSR: 0x%X\n", val);
+#endif
+		write_SSPCPSR(val, drv_data->ioaddr);
+
+		val = read_SSPCR0(drv_data->ioaddr);
+		val = val & ~SSPCR0_SCR_MASK;
+		val = val | (div_scr << SSPCR0_SCR_SHIFT);
+#ifdef SPI_EP93XX_DEBUG
+		dev_info (&spi->dev, "SSPCR0: 0x%X (div_scr: 0x%X)\n", val, div_scr);
+#endif
+		write_SSPCR0(val, drv_data->ioaddr);
+	} else
+		return -EINVAL;
+
+	/* reenable */
+	val = read_SSPCR1(drv_data->ioaddr);
+	val = val & ~SSPC1_SSE;
+	write_SSPCR1(val, drv_data->ioaddr);
+	val = read_SSPCR1(drv_data->ioaddr);
+	val = val | SSPC1_SSE;
+	write_SSPCR1(val, drv_data->ioaddr);
+#ifdef SPI_EP93XX_DEBUG
+	dev_info (&spi->dev, "Loopback mode: %s\n", (val & SSPC1_LBM) ? "On" : "Off");
+#endif
+
+	return 0;
+}
+
+static int ep93xx_spi_transfer(struct spi_device *spi, struct spi_message *m)
+{
+	struct driver_data *drv_data;
+	unsigned long flags;
+	int status = 0;
+
+	m->actual_length = 0;
+	m->status = -EINPROGRESS;
+
+	drv_data = spi_master_get_devdata(spi->master);
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	if (!spi->max_speed_hz)
+		status = -ENETDOWN;
+	else {
+		list_add_tail(&m->queue, &drv_data->queue);
+		queue_work(drv_data->workqueue, &drv_data->work);
+	}
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+	return status;
+}
+
+static void ep93xx_work(struct work_struct *work)
+{
+	struct driver_data *drv_data =
+		container_of(work, struct driver_data, work);
+	unsigned long flags;
+
+	spin_lock_irqsave(&drv_data->lock, flags);
+	drv_data->busy = 1;
+
+	while (!list_empty(&drv_data->queue)) {
+		struct spi_message *m;
+		struct spi_device *spi;
+		struct spi_transfer *t = NULL;
+		int status;
+
+		m = container_of(drv_data->queue.next, struct spi_message,
+			queue);
+		list_del_init(&m->queue);
+		spin_unlock_irqrestore(&drv_data->lock, flags);
+
+		spi = m->spi;
+		status = 0;
+
+		list_for_each_entry(t, &m->transfers, transfer_list) {
+
+			if (!t->tx_buf && !t->rx_buf && t->len) {
+				status = -EINVAL;
+				break;
+			}
+
+			if (t->len) {
+				if (!m->is_dma_mapped) {
+					t->rx_dma = 0;
+					t->tx_dma = 0;
+				}
+				if (t->bits_per_word <= 8)
+					status = ep93xx_txrx_8(spi, t);
+				else
+					status = ep93xx_txrx_16(spi, t);
+			}
+
+			if (status != t->len) {
+				if (status > 0)
+					status = -EMSGSIZE;
+				break;
+			}
+			m->actual_length += status;
+			status = 0;
+
+			/* protocol tweaks before next transfer */
+			if (t->delay_usecs)
+				udelay(t->delay_usecs);
+
+			if (t->transfer_list.next == &m->transfers)
+				break;
+		}
+
+		m->status = status;
+		m->complete(m->context);
+
+		spin_lock_irqsave(&drv_data->lock, flags);
+	}
+	drv_data->busy = 0;
+	spin_unlock_irqrestore(&drv_data->lock, flags);
+}
+
+static irqreturn_t ssp_int(int irq, void *dev_id)
+{
+	struct driver_data *drv_data = dev_id;
+	u8 status;
+	status = read_SSPIIR(drv_data->ioaddr);
+
+	if (status & SSPIIR_RORIS) {
+		dev_err(&drv_data->pdev->dev, "SPI rx overrun.\n");
+
+		/* We clear the overrun here ! */
+		write_SSPICR(0, drv_data->ioaddr);
+	}
+
+	/* RX interrupt */
+	if (status & SSPIIR_RIS)
+		dev_info(&drv_data->pdev->dev, "SPI RX interrupt\n");
+
+	/* TX interrupt */
+	if (status & SSPIIR_TIS)
+		dev_info(&drv_data->pdev->dev, "SPI TX interrupt\n");
+
+	write_SSPICR(0, drv_data->ioaddr);
+	return IRQ_HANDLED;
+}
+
+static int __init ep93xx_spi_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct spi_master *master;
+	struct driver_data *drv_data = 0;
+	struct resource *memory_resource;
+	int status = 0;
+	u16 val;
+
+	/* Allocate master with space for drv_data and null dma buffer */
+	master = spi_alloc_master(dev, sizeof(struct driver_data));
+	if (!master) {
+		dev_err(&pdev->dev, "cannot alloc spi_master\n");
+		return -ENOMEM;
+	}
+	drv_data = spi_master_get_devdata(master);
+	drv_data->master = master;
+	drv_data->pdev = pdev;
+
+        master->num_chipselect = EP93XX_GPIO_LINE_H(7) + 1;
+	master->bus_num = pdev->id;
+	master->setup = ep93xx_spi_setup;
+	master->transfer = ep93xx_spi_transfer;
+
+	spin_lock_init(&drv_data->lock);
+	INIT_LIST_HEAD(&drv_data->queue);
+
+	/* Setup register addresses */
+	memory_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!memory_resource) {
+	    dev_err(&pdev->dev, "memory resources not defined\n");
+	    status = -EIO;
+	    goto out_error_master_alloc;
+	} else {
+	    drv_data->ioaddr = ioremap(memory_resource->start,
+				memory_resource->end - memory_resource->start);
+	    if (drv_data->ioaddr == NULL) {
+		    dev_err(&pdev->dev, "ioremap failed\n");
+		    status = -EIO;
+		    goto out_error_master_alloc;
+	    }
+	}
+
+	/* Attach to IRQ */
+	drv_data->irq = platform_get_irq(pdev, 0);
+	if (drv_data->irq < 0)
+		return drv_data->irq;
+
+	if (drv_data->irq <= 0) {
+		dev_err(&pdev->dev, "IRQ resource not defined\n");
+		status = -ENODEV;
+		goto out_error_master_alloc;
+	}
+
+	status = request_irq(drv_data->irq, ssp_int, 0, "ep93xx-spi", drv_data);
+	if (status < 0) {
+		dev_err(&pdev->dev, "cannot get SPI IRQ 0\n");
+		goto out_error_master_alloc;
+	}
+
+	/* SSP default configuration, enable */
+	write_SSPCR1(SSPC1_SSE, drv_data->ioaddr);
+
+	/* run as master */
+	val = read_SSPCR1(drv_data->ioaddr);
+	val = val & ~SSPC1_MS;
+	write_SSPCR1(val, drv_data->ioaddr);
+
+	/* frame format to Motorola SPI Format */
+	val = read_SSPCR0(drv_data->ioaddr);
+	val = val & ~SSPCR0_FRF_MASK ;
+	val = val | SSPCR0_FRF_MOTOROLA;
+	write_SSPCR0(val, drv_data->ioaddr);
+
+	/* enable interrupts */
+	val = read_SSPCR1(drv_data->ioaddr);
+	/* for now only overrun is handled */
+	/* val = val | SSPC1_RIE | SSPC1_TIE | SSPC1_RORIE; */
+	val = val | SSPC1_RORIE;
+	write_SSPCR1(val, drv_data->ioaddr);
+
+	/* SSP default configuration, re enable */
+	val = read_SSPCR1(drv_data->ioaddr);
+	val = val & ~SSPC1_SSE;
+	write_SSPCR1(val, drv_data->ioaddr);
+	val = read_SSPCR1(drv_data->ioaddr);
+	val = val | SSPC1_SSE;
+	write_SSPCR1(val, drv_data->ioaddr);
+
+	/* Register with the SPI framework */
+	platform_set_drvdata(pdev, drv_data);
+	status = spi_register_master(master);
+	if (status != 0) {
+		dev_err(&pdev->dev, "cannot register SPI master\n");
+		goto out_error_master_alloc;
+	} else
+		dev_info(&pdev->dev, "SPI Controller initalized\n");
+
+	INIT_WORK(&drv_data->work, ep93xx_work);
+	spin_lock_init(&drv_data->lock);
+	INIT_LIST_HEAD(&drv_data->queue);
+
+	/* this task is the only thing to touch the SPI bits */
+	drv_data->busy = 0;
+	drv_data->workqueue = create_singlethread_workqueue(
+		dev_name(drv_data->master->dev.parent));
+/*              drv_data->master->cdev.dev->bus_id); */
+	if (drv_data->workqueue == NULL) {
+		status = -EBUSY;
+		goto out_error_free_irq;
+	}
+
+	return status;
+
+out_error_free_irq:
+	free_irq(drv_data->irq, master);
+out_error_master_alloc:
+	if (drv_data->ioaddr != NULL)
+		iounmap(drv_data->ioaddr);
+	spi_master_put(master);
+	return status;
+}
+
+static int __exit ep93xx_spi_remove(struct platform_device *pdev)
+{
+	struct driver_data *drv_data = platform_get_drvdata(pdev);
+	u8 val;
+
+	WARN_ON(!list_empty(&drv_data->queue));
+
+	destroy_workqueue(drv_data->workqueue);
+
+	/* switch off SSP*/
+	val = read_SSPCR1(drv_data->ioaddr);
+	val = val & ~SSPC1_SSE;
+	write_SSPCR1(val, drv_data->ioaddr);
+
+	/* release irqs */
+	if (drv_data->irq > 0)
+		free_irq(drv_data->irq, drv_data);
+
+	/* Disconnect from the SPI framework */
+	spi_unregister_master(drv_data->master);
+	spi_master_put(drv_data->master);
+
+	if (drv_data->ioaddr != NULL)
+		iounmap(drv_data->ioaddr);
+
+	/* Prevent double remove */
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int ep93xx_spi_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+	return 0;
+}
+
+static int ep93xx_spi_resume(struct platform_device *pdev)
+{
+	return 0;
+}
+
+#else
+#define ep93xx_spi_suspend NULL
+#define ep93xx_spi_resume  NULL
+#endif
+
+struct platform_driver ep93xx_spi_device = {
+	.remove		= __exit_p(ep93xx_spi_remove),
+#ifdef CONFIG_PM
+	.suspend	= ep93xx_spi_suspend,
+	.resume		= ep93xx_spi_resume,
+#endif
+	.driver		= {
+		.name	= "ep93xx-spi",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+};
+
+int __init ep93xx_spi_init(void)
+{
+	return platform_driver_probe(&ep93xx_spi_device, ep93xx_spi_probe);
+}
+
+void __exit ep93xx_spi_exit(void)
+{
+	platform_driver_unregister(&ep93xx_spi_device);
+}
+
+module_init(ep93xx_spi_init);
+module_exit(ep93xx_spi_exit);
+
+MODULE_DESCRIPTION("EP93XX SPI Driver");
+MODULE_AUTHOR("Manfred Gruber, <m.gruber@tirol.com>");
+MODULE_LICENSE("GPL");