diff -Naur linux-2.6.30.4-orig/arch/arm/Kconfig linux-2.6.30.4-new/arch/arm/Kconfig
--- linux-2.6.30.4-orig/arch/arm/Kconfig	2009-08-21 14:55:08.271390000 -0700
+++ linux-2.6.30.4-new/arch/arm/Kconfig	2009-08-21 14:55:42.758662000 -0700
@@ -1383,6 +1383,8 @@
 
 source "drivers/spi/Kconfig"
 
+source "drivers/spi2/Kconfig"
+
 source "drivers/gpio/Kconfig"
 
 source "drivers/w1/Kconfig"
diff -Naur linux-2.6.30.4-orig/drivers/Makefile linux-2.6.30.4-new/drivers/Makefile
--- linux-2.6.30.4-orig/drivers/Makefile	2009-07-30 15:34:47.000000000 -0700
+++ linux-2.6.30.4-new/drivers/Makefile	2009-08-21 14:55:42.773662000 -0700
@@ -52,6 +52,7 @@
 obj-y				+= auxdisplay/
 obj-$(CONFIG_MTD)		+= mtd/
 obj-$(CONFIG_SPI)		+= spi/
+obj-$(CONFIG_SPI_MSPD)		+= spi2/
 obj-$(CONFIG_PCCARD)		+= pcmcia/
 obj-$(CONFIG_DIO)		+= dio/
 obj-$(CONFIG_SBUS)		+= sbus/
diff -Naur linux-2.6.30.4-orig/drivers/spi2/busses/comcerto_spi.c linux-2.6.30.4-new/drivers/spi2/busses/comcerto_spi.c
--- linux-2.6.30.4-orig/drivers/spi2/busses/comcerto_spi.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/drivers/spi2/busses/comcerto_spi.c	2009-08-21 14:55:42.781663000 -0700
@@ -0,0 +1,729 @@
+/*
+ *  drivers/spi2/busses/comcerto_spi.c
+ *
+ *  Copyright (C) 2004,2005 Mindspeed Technologies, Inc.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/version.h>
+#if !defined (AUTOCONF_INCLUDED)
+#include <linux/config.h>
+#endif
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+#include <mach/irqs.h>
+
+#include <linux/platform_device.h>
+#include "comcerto_spi.h"
+
+/**
+ * do_write_read_transfer8 -
+ *
+ *
+ */
+static int do_write_read_transfer8(struct comcerto_spi *spi, u8 *wbuf, unsigned int *wlen, u8 *rbuf, unsigned int *rlen)
+{
+	unsigned int len_now;
+	int rc = 0;
+	unsigned int wtmp = *wlen, rtmp = *rlen;
+	u32 dr = spi->membase + COMCERTO_SPI_DR;
+	u32 txflr = spi->membase + COMCERTO_SPI_TXFLR;
+	u32 rxflr = spi->membase + COMCERTO_SPI_RXFLR;
+
+//	printk(KERN_INFO "do_write_read_transfer(%#lx, %#lx, %d, %#lx, %d)\n", (unsigned long)spi,
+//                                                                      (unsigned long)wbuf, *wlen,
+//                                                                      (unsigned long)rbuf, *rlen);
+
+	while (wtmp || rtmp) {
+		len_now = 8 - __raw_readl(txflr);
+		if (len_now > wtmp)
+			len_now = wtmp;
+
+		wtmp -= len_now;
+
+		/* warm-up write fifo to avoid underruns */
+		while (len_now--)
+			__raw_writew(cpu_to_le16((u16) *wbuf++), dr);
+
+
+		len_now = __raw_readl(rxflr);
+		if (len_now > rtmp)
+			len_now = rtmp;
+
+		rtmp -= len_now;
+
+		while (len_now--) {
+			*rbuf = (u8) (le16_to_cpu(__raw_readw(dr)) & 0xff);
+			rbuf++;
+		}
+	}
+
+	*rlen -= rtmp;
+	*wlen -= wtmp;
+
+	return rc;
+}
+
+/**
+ * do_write_read_transfer16 -
+ *
+ *
+ */
+static int do_write_read_transfer16(struct comcerto_spi *spi, u16 *wbuf, unsigned int *wlen, u16 *rbuf, unsigned int *rlen)
+{
+	unsigned int len_now;
+	int rc = 0;
+	unsigned int wtmp = *wlen, rtmp = *rlen;
+	unsigned int wpadding, rpadding;
+	u32 dr = spi->membase + COMCERTO_SPI_DR;
+	u32 txflr = spi->membase + COMCERTO_SPI_TXFLR;
+	u32 rxflr = spi->membase + COMCERTO_SPI_RXFLR;
+
+//	printk(KERN_INFO "do_write_read_transfer(%#lx, %#lx, %d, %#lx, %d)\n", (unsigned long)spi
+//                                                                      (unsigned long)wbuf, *wlen,
+//                                                                      (unsigned long)rbuf, *rlen);
+
+	if (wtmp > rtmp) {
+		wpadding  = 0;
+		rpadding = wtmp - rtmp;
+	} else {
+		wpadding = rtmp - wtmp;
+		rpadding = 0;
+	}
+
+	while (wtmp || rtmp) {
+		len_now = 8 - __raw_readl(txflr);
+
+		if (wtmp) {
+			if (len_now > wtmp)
+				len_now = wtmp;
+
+			wtmp -= len_now;
+
+			while (len_now--)
+				__raw_writew(cpu_to_le16(*wbuf++), dr);
+
+		} else if (wpadding) {
+			if (len_now > wpadding)
+				len_now = wpadding;
+
+			wpadding -= len_now;
+
+			while (len_now--)
+				__raw_writew(0, dr);
+		}
+
+		len_now = __raw_readl(rxflr);
+		if (rtmp) {
+			if (len_now > rtmp)
+				len_now = rtmp;
+
+			rtmp -= len_now;
+
+			while (len_now--) {
+				*rbuf = le16_to_cpu(__raw_readw(dr));
+				rbuf++;
+			}
+		} else if (rpadding) {
+			if (len_now > rpadding)
+				len_now = rpadding;
+
+			rpadding -= len_now;
+
+			while (len_now--)
+				__raw_readw(dr);
+		}
+	}
+
+	*rlen -= rtmp;
+	*wlen -= wtmp;
+
+	return rc;
+}
+
+
+/**
+ * do_write_only_transfer8 -
+ *
+ *
+ */
+static int do_write_only_transfer8(struct comcerto_spi *spi, u8 *buf, unsigned int *len)
+{
+	unsigned int len_now;
+	int rc = 0;
+	unsigned int tmp = *len;
+	u32 dr = spi->membase + COMCERTO_SPI_DR;
+	u32 txflr = spi->membase + COMCERTO_SPI_TXFLR;
+
+//	printk(KERN_INFO "do_write_only_transfer8(%#lx, %#lx, %d)\n", (unsigned long)spi, (unsigned long)buf, *len);
+
+	while (tmp) {
+		len_now = 8 - __raw_readl(txflr);
+		if (len_now > tmp)
+			len_now = tmp;
+
+		tmp -= len_now;
+
+		while (len_now--)
+			__raw_writew(cpu_to_le16((u16) *buf++), dr);
+	}
+
+	*len -= tmp;
+
+//      printk(KERN_INFO "exit do_write_only_transfer(%d, %d)\n", *len, rc);
+
+	return rc;
+}
+
+/**
+ * do_write_only_transfer -
+ *
+ *
+ */
+static int do_write_only_transfer16(struct comcerto_spi *spi, u16 *buf, unsigned int *len)
+{
+	unsigned int len_now;
+	int rc = 0;
+	unsigned int tmp = *len;
+	u32 dr = spi->membase + COMCERTO_SPI_DR;
+	u32 txflr = spi->membase + COMCERTO_SPI_TXFLR;
+
+//      printk(KERN_INFO "do_write_only_transfer(%#lx, %#lx, %d)\n", (unsigned long)spi, (unsigned long)buf, *len);
+
+	while (tmp) {
+		len_now = 8 - __raw_readl(txflr);
+		if (len_now > tmp)
+			len_now = tmp;
+
+		tmp -= len_now;
+
+		while (len_now--)
+			__raw_writew(cpu_to_le16(*buf++), dr);
+	}
+
+	*len -= tmp;
+
+//      printk(KERN_INFO "exit do_write_only_transfer(%d, %d)\n", *len, rc);
+
+	return rc;
+}
+
+
+/**
+ * do_read_only_transfer -
+ *
+ *
+ */
+static int do_read_only_transfer8(struct comcerto_spi *spi, u8 *buf, unsigned int *len)
+{
+	unsigned int len_now;
+	int rc = 0;
+	unsigned int tmp = *len;
+	u32 dr = spi->membase + COMCERTO_SPI_DR;
+	u32 rxflr = spi->membase + COMCERTO_SPI_RXFLR;
+
+//	printk(KERN_INFO "do_read_only_transfer8(%#lx, %#lx, %d)\n", (unsigned long)spi, (unsigned long)buf, *len);
+
+	/* start the serial clock */
+	__raw_writew(0, dr);
+
+	while (tmp) {
+		len_now = __raw_readl(rxflr);
+		if (len_now > tmp)
+			len_now = tmp;
+
+		tmp -= len_now;
+
+		while (len_now--) {
+			*buf = (u8) (le16_to_cpu(__raw_readw(dr)) & 0xff);
+			buf++;
+		}
+	}
+
+	*len -= tmp;
+
+	return rc;
+}
+
+/**
+ * do_read_only_transfer -
+ *
+ *
+ */
+static int do_read_only_transfer16(struct comcerto_spi *spi, u16 *buf, unsigned int *len)
+{
+	unsigned int len_now;
+	int rc = 0;
+	unsigned int tmp = *len;
+	u32 dr = spi->membase + COMCERTO_SPI_DR;
+	u32 rxflr = spi->membase + COMCERTO_SPI_RXFLR;
+
+//      printk(KERN_INFO "do_read_only_transfer(%#lx, %#lx, %d)\n", (unsigned long)spi, (unsigned long)buf, *len);
+
+	/* start the serial clock */
+	__raw_writew(0, dr);
+
+	while (tmp) {
+		len_now = __raw_readl(rxflr);
+		if (len_now > tmp)
+			len_now = tmp;
+
+		tmp -= len_now;
+
+		while (len_now--) {
+			*buf = le16_to_cpu(__raw_readw(dr));
+			buf++;
+		}
+	}
+
+	*len -= tmp;
+
+	return rc;
+}
+
+
+/**
+ * comcerto_spi_do_transfer -
+ *
+ *
+ */
+int comcerto_spi_do_transfer(struct spi_adapter *adapter, struct spi_transfer *transfer, struct spi_client_config *config)
+{
+	struct comcerto_spi *spi = (struct comcerto_spi *)adapter->data;
+	u32 ctrlr0, ctrlr1, baudr, ser;
+	int rc;
+
+//      printk(KERN_INFO "comcerto_spi_do_transfer(%#lx, %#lx, %#lx)\n", (unsigned long) adapter, (unsigned long) transfer, (unsigned long) config);
+
+	/* make sure last transaction is finished */
+	while (__raw_readl(spi->membase + COMCERTO_SPI_SR) & BUSY) ;
+
+	ctrlr0 = ((config->sc_polarity & 0x1) << 7) | ((config->sc_phase & 0x1) << 6) | (((transfer->fs - 1) & 0xf) << 0);
+
+	baudr = spi->clock_rate / config->sc_rate;
+
+	ser = config->cs_msk & adapter->caps.cs_msk;
+
+	__raw_writel(0, spi->membase + COMCERTO_SPI_SSIENR);
+
+	switch (transfer->mode & 0x0f) {
+	default:
+		rc = -1;
+		break;
+
+	case SPI_TRANSFER_MODE_WRITE_ONLY:
+		ctrlr0 |= (0x0001 << 8);
+
+		__raw_writel(ctrlr0, spi->membase + COMCERTO_SPI_CTRLR0);
+		__raw_writel(baudr, spi->membase + COMCERTO_SPI_BAUDR);
+		__raw_writel(ser, spi->membase + COMCERTO_SPI_SER);
+		__raw_writel(8, spi->membase + COMCERTO_SPI_RXFTLR);
+		__raw_writel(0, spi->membase + COMCERTO_SPI_TXFTLR);
+		__raw_writel(0, spi->membase + COMCERTO_SPI_IMR);
+		__raw_writel(1, spi->membase + COMCERTO_SPI_SSIENR);
+
+		if (transfer->fs <= 8)
+			rc = do_write_only_transfer8(spi, transfer->wbuf, &transfer->wlen);
+		else
+			rc = do_write_only_transfer16(spi, (u16 *) transfer->wbuf, &transfer->wlen);
+
+		break;
+
+	case SPI_TRANSFER_MODE_READ_ONLY:
+		ctrlr0 |= (0x0002 << 8);
+		ctrlr1 = transfer->rlen - 1;
+
+		__raw_writel(ctrlr0, spi->membase + COMCERTO_SPI_CTRLR0);
+		__raw_writel(ctrlr1, spi->membase + COMCERTO_SPI_CTRLR1);
+		__raw_writel(baudr, spi->membase + COMCERTO_SPI_BAUDR);
+		__raw_writel(ser, spi->membase + COMCERTO_SPI_SER);
+		__raw_writel(8, spi->membase + COMCERTO_SPI_RXFTLR);
+		__raw_writel(0, spi->membase + COMCERTO_SPI_TXFTLR);
+		__raw_writel(0, spi->membase + COMCERTO_SPI_IMR);
+		__raw_writel(1, spi->membase + COMCERTO_SPI_SSIENR);
+
+		if (transfer->fs <= 8)
+			rc = do_read_only_transfer8(spi, transfer->rbuf, &transfer->rlen);
+		else
+			rc = do_read_only_transfer16(spi, (u16 *) transfer->rbuf, &transfer->rlen);
+	
+		break;
+
+	case SPI_TRANSFER_MODE_WRITE_READ:
+		ctrlr0 |= (0x0000 << 8);
+
+		__raw_writel(ctrlr0, spi->membase + COMCERTO_SPI_CTRLR0);
+		__raw_writel(baudr, spi->membase + COMCERTO_SPI_BAUDR);
+		__raw_writel(ser, spi->membase + COMCERTO_SPI_SER);
+		__raw_writel(8, spi->membase + COMCERTO_SPI_RXFTLR);
+		__raw_writel(0, spi->membase + COMCERTO_SPI_TXFTLR);
+		__raw_writel(0, spi->membase + COMCERTO_SPI_IMR);
+		__raw_writel(1, spi->membase + COMCERTO_SPI_SSIENR);
+
+		if (transfer->fs <= 8)
+			rc = do_write_read_transfer8(spi, transfer->wbuf, &transfer->wlen, transfer->rbuf, &transfer->rlen);
+		else
+			rc = do_write_read_transfer16(spi, (u16 *) transfer->wbuf, &transfer->wlen, (u16 *) transfer->rbuf, &transfer->rlen);
+
+		break;
+	}
+
+	return rc;
+}
+
+#if 0
+/**
+ * comcerto_spi_irq_handler -
+ *
+ *
+ */
+irqreturn_t comcerto_spi_irq_handler(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct comcerto_spi *spi = (struct comcerto_spi *)dev_id;
+	struct spi_adapter *adapter = spi->adapter;
+	u32 callback_status = 0;
+	u32 status;
+	u32 imr;
+	irqreturn_t ret = IRQ_NONE;
+
+	printk(KERN_INFO "comcerto_spi_irq_handler(%d, %#lx, %#lx)\n", irq, (unsigned long)dev_id, (unsigned long)regs);
+
+	status = readl(spi->membase + COMCERTO_SPI_ISR);
+	if (!status)
+		goto out;
+
+	printk(KERN_INFO "status %x\n", status);
+
+	ret = IRQ_HANDLED;
+
+	if (status & TXEIS) {
+		callback_status |= SPI_WRITE_DONE;
+
+		printk(KERN_INFO "%x %x\n", readl(spi->membase + COMCERTO_SPI_SR), readl(spi->membase + COMCERTO_SPI_TXFLR));
+
+		/* disable fifo empty interrupt */
+		imr = readl(spi->membase + COMCERTO_SPI_IMR) & ~(TXEIM);
+		writel(imr, spi->membase + COMCERTO_SPI_IMR);
+	}
+
+	if (status & TXOIS) {
+		callback_status |= SPI_WRITE_ERROR;
+	}
+
+	if (status & RXUIS) {
+		callback_status |= SPI_READ_ERROR;
+	}
+
+	if (status & RXOIS) {
+		callback_status |= SPI_READ_ERROR;
+	}
+
+	if (status & RXFIS) {
+		callback_status |= SPI_DATA_AVAILABLE;
+	}
+
+	spi_callback(adapter, callback_status);
+
+	/* clear all interrupts */
+	readl(spi->membase + COMCERTO_SPI_ICR);
+
+      out:
+	return ret;
+}
+#endif
+
+/**
+ * comcerto_spi_hw_init -
+ *
+ *
+ */
+static void comcerto_spi_hw_init(struct comcerto_spi *spi)
+{
+//	printk(KERN_INFO "comcerto_spi_hw_init(%#lx)\n", (unsigned long)spi);
+
+	/* enable SPI bus */
+	comcerto_gpio_ctrl(0x3 << 4, 0x3 << 4);
+
+	/* disable SPI operation */
+	writel(0, spi->membase + COMCERTO_SPI_SSIENR);
+
+	/* mask all SPI irq's */
+	writel(0, spi->membase + COMCERTO_SPI_IMR);
+}
+
+/**
+ * comcerto_spi_hw_reset -
+ *
+ *
+ */
+static void comcerto_spi_hw_reset(struct comcerto_spi *spi)
+{
+	/* disable SPI operation */
+	writel(0, spi->membase + COMCERTO_SPI_SSIENR);
+
+	/* mask all SPI irq's */
+	writel(0, spi->membase + COMCERTO_SPI_IMR);
+
+	/* disable SPI bus */
+	comcerto_gpio_ctrl(0x0 << 4, 0x3 << 4);
+}
+
+struct spi_adapter comcerto_spi_adapter = {
+	.name = "comcerto-spi",
+	.do_transfer = comcerto_spi_do_transfer,
+};
+
+/**
+* comcerto_spi_probe -
+ *
+ *
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+static int __init comcerto_spi_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+#else
+static int __init comcerto_spi_probe(struct platform_device *pdev)
+{
+#endif
+	struct comcerto_spi *spi;
+	unsigned long base, len;
+
+//	printk(KERN_INFO "comcerto_spi_probe(%#lx)\n", (unsigned long) dev);
+
+	spi = kmalloc(sizeof(struct comcerto_spi), GFP_KERNEL);
+	if (spi == NULL) {
+		printk(KERN_INFO "comcerto_spi: error allocating memory");
+		goto err0;
+	}
+
+	base = pdev->resource[0].start;
+	len = pdev->resource[0].end - pdev->resource[0].start + 1;
+
+	if (!request_mem_region(base, len, COMCERTO_SPI_DRIVER_NAME)) {
+		printk(KERN_INFO "comcerto_spi: error requesting memory region %#lx - %#lx", base, base + len);
+		goto err1;
+	}
+
+	/* io-remaped in arch/arm/mm.c */
+	spi->membase = APB_VADDR(pdev->resource[0].start);
+	spi->irq = pdev->resource[1].start;
+
+	comcerto_spi_hw_init(spi);
+#if 0
+	if (request_irq(spi->irq, comcerto_spi_irq_handler, SA_SHIRQ, COMCERTO_SPI_DRIVER_NAME, spi)) {
+		printk(KERN_INFO "comcerto_spi: error requesting irq %d\n", IRQ_SPI);
+		goto err2;
+	}
+#endif
+	spi->adapter = &comcerto_spi_adapter;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	comcerto_spi_adapter.dev.parent = &pdev->dev;
+#endif
+	comcerto_spi_adapter.data = spi;
+	comcerto_spi_adapter.caps.max_sc_rate = (COMCERTO_AHBCLK * 1000000) / 2;
+	comcerto_spi_adapter.caps.min_sc_rate = (COMCERTO_AHBCLK * 1000000) / 0xffff;
+	comcerto_spi_adapter.caps.max_fs = 16;
+	comcerto_spi_adapter.caps.min_fs = 4;
+	comcerto_spi_adapter.caps.max_nframe = 0xffff;
+	comcerto_spi_adapter.caps.min_nframe = 1;
+	comcerto_spi_adapter.caps.cs_msk = 0xf;
+
+	if (spi_add_adapter(&comcerto_spi_adapter)) {
+		printk(KERN_INFO "comcerto_spi: error adding adapter\n");
+		goto err3;
+	}
+
+	spi->clock_rate = (COMCERTO_AHBCLK * 1000000);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	dev_set_drvdata(&pdev->dev, spi);
+#else
+	pdev->data = (unsigned long) spi;
+#endif
+	return 0;
+
+      err3:
+#if 0
+	free_irq(spi->irq, spi);
+
+      err2:
+#endif
+	release_mem_region(base, len);
+
+      err1:
+	kfree(spi);
+
+      err0:
+	return -1;
+}
+
+/**
+ * comcerto_spi_remove -
+ *
+ *
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+static int comcerto_spi_remove(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct comcerto_spi *spi = dev_get_drvdata(&pdev->dev);
+	unsigned long base, len;
+
+	dev_set_drvdata(&pdev->dev, NULL);
+#else
+static int comcerto_spi_remove(struct platform_device *pdev)
+{
+	struct comcerto_spi *spi = (struct comcerto_spi *) pdev->data;
+	unsigned long base, len;
+#endif
+
+	spi_del_adapter(spi->adapter);
+
+	comcerto_spi_hw_reset(spi);
+
+//      free_irq(spi->irq, spi);
+
+	base = pdev->resource[0].start;
+	len = pdev->resource[0].end - pdev->resource[0].start + 1;
+
+	release_mem_region(base, len);
+
+	kfree(spi);
+
+	return 0;
+}
+
+static struct resource comcerto_spi_resources[] = {
+	{
+	 .start = COMCERTO_APB_SPI_BASE,
+	 .end = COMCERTO_APB_SPI_BASE + SZ_4K,
+	 .flags = IORESOURCE_MEM,
+	 },
+	{
+	 .start = IRQ_SPI,
+	 .flags = IORESOURCE_IRQ,
+	 }
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+static struct device_driver comcerto_spi_driver = {
+	.name = "comcerto-spi",
+	.bus = &platform_bus_type,
+	.probe = comcerto_spi_probe,
+	.remove = comcerto_spi_remove,
+};
+
+static void comcerto_spi_release(struct device *dev)
+{
+	/* Just to keep driver model happy */
+}
+
+static struct platform_device comcerto_spi_device = {
+	.name = "comcerto-spi",
+	.id = -1,
+	.num_resources = ARRAY_SIZE(comcerto_spi_resources),
+	.resource = comcerto_spi_resources,
+	.dev = {
+		.release = comcerto_spi_release,
+	},
+};
+
+
+/**
+ * comcerto_spi_init -
+ *
+ *
+ */
+static int __init comcerto_spi_init(void)
+{
+//	printk(KERN_INFO "comcerto_spi_init()\n");
+
+	if (driver_register(&comcerto_spi_driver)) {
+		printk(KERN_INFO "comcerto_spi: error registering driver\n");
+		goto err0;
+	}
+
+	if (platform_device_register(&comcerto_spi_device)) {
+		printk(KERN_INFO "comcerto_spi: error registering device\n");
+		goto err1;
+	}
+
+	return 0;
+
+      err1:
+	driver_unregister(&comcerto_spi_driver);
+
+      err0:
+	return -1;
+}
+
+/**
+ * comcerto_spi_exit -
+ *
+ *
+ */
+static void __exit comcerto_spi_exit(void)
+{
+	platform_device_unregister(&comcerto_spi_device);
+	driver_unregister(&comcerto_spi_driver);
+}
+
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */ 
+
+static struct platform_device comcerto_spi_device = {
+	.name = "comcerto-spi",
+	.id = -1,
+	.num_resources = ARRAY_SIZE(comcerto_spi_resources),
+	.resource = comcerto_spi_resources,
+};
+
+/**
+ *  comcerto_spi_init -
+ *
+ *
+ */
+static int __init comcerto_spi_init(void)
+{
+	if (comcerto_spi_probe(&comcerto_spi_device)) {
+		printk(KERN_INFO "comcerto_spi: error probing device\n");
+		goto err;
+	}
+
+	return 0;
+
+  err:
+	return -1;
+}
+
+/**
+ * comcerto_spi_exit -
+ *
+ *
+ */
+static void __exit comcerto_spi_exit(void)
+{
+	comcerto_spi_remove(&comcerto_spi_device);
+}
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */
+
+MODULE_AUTHOR("Mindspeed Technologies, Inc.");
+MODULE_DESCRIPTION("Comcerto SPI bus driver");
+MODULE_LICENSE("GPL");
+
+module_init(comcerto_spi_init);
+module_exit(comcerto_spi_exit);
diff -Naur linux-2.6.30.4-orig/drivers/spi2/busses/comcerto_spi.h linux-2.6.30.4-new/drivers/spi2/busses/comcerto_spi.h
--- linux-2.6.30.4-orig/drivers/spi2/busses/comcerto_spi.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/drivers/spi2/busses/comcerto_spi.h	2009-08-21 14:55:42.785662000 -0700
@@ -0,0 +1,99 @@
+/*
+ *  linux/drivers/spi2/busses/comcerto_spi.h
+ *
+ *  Copyright (C) Mindspeed Technologies
+ *
+ * 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.
+ *
+ */
+#ifndef _COMCERTO_SPI_H
+#define _COMCERTO_SPI_H
+
+#include <linux/version.h>
+#include <linux/spi2/spi.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#include <linux/ioport.h>
+
+struct platform_device {
+	char            *name;
+	u32             id;
+	u32             num_resources;
+	struct resource *resource;
+
+	unsigned long data;
+};
+#endif
+
+#define COMCERTO_SPI_DRIVER_NAME	"Comcerto SPI"
+
+
+#define COMCERTO_SPI_CTRLR0               0x00
+#define COMCERTO_SPI_CTRLR1               0x04
+#define COMCERTO_SPI_SSIENR               0x08
+#define COMCERTO_SPI_MWCR                 0x0C
+#define COMCERTO_SPI_SER                  0x10
+#define COMCERTO_SPI_BAUDR                0x14
+#define COMCERTO_SPI_TXFTLR               0x18
+#define COMCERTO_SPI_RXFTLR               0x1C
+#define COMCERTO_SPI_TXFLR                0x20
+#define COMCERTO_SPI_RXFLR                0x24
+#define COMCERTO_SPI_SR                   0x28
+#define COMCERTO_SPI_IMR                  0x2C
+#define COMCERTO_SPI_ISR                  0x30
+#define COMCERTO_SPI_RISR                 0x34
+#define COMCERTO_SPI_TXOICR               0x38
+#define COMCERTO_SPI_RXOICR               0x3C
+#define COMCERTO_SPI_RXUICR               0x40
+#define COMCERTO_SPI_MSTICR               0x44
+#define COMCERTO_SPI_ICR                  0x48
+#define COMCERTO_SPI_IDR                  0x58
+#define COMCERTO_SPI_DR                   0x60
+
+
+/* SR - status register bits */
+#define BUSY		(1<<0)	/* SSI busy flag, serial transfer in progress */
+#define TFNF		(1<<1)	/* Transmit FIFO not full */
+#define TFE		(1<<2)	/* Transmit FIFO empty */
+#define RFNE		(1<<3)	/* Receive FIFO not empty */
+#define RFF		(1<<4)	/* Receive FIFO full */
+#define TXE		(1<<5)	/* Transmission error */
+#define DCOL		(1<<6)	/* Data collision error */
+
+/* Interrupt status after being masked */
+#define TXEIS		(1<<0)	/* Transmit FIFO empty interrupt status */
+#define TXOIS		(1<<1)	/* Transmit FIFO overflow interrupt status */
+#define RXUIS		(1<<2)	/* Receive FIFO underflow interrupt status */
+#define RXOIS		(1<<3)	/* Receive FIFO overflow interrupt status */
+#define RXFIS		(1<<4)	/* Receive FIFO full interrupt status */
+#define MSTIS		(1<<5)	/* Multi-Master contention interrupt status */
+
+/* Interrupt status before being masked */
+#define TXEIR		(1<<0)	/* Transmit FIFO empty interrupt status */
+#define TXOIR		(1<<1)	/* Transmit FIFO overflow interrupt status */
+#define RXUIR		(1<<2)	/* Receive FIFO underflow interrupt status */
+#define RXOIR		(1<<3)	/* Receive FIFO overflow interrupt status */
+#define RXFIR		(1<<4)	/* Receive FIFO full interrupt status */
+#define MSTIR		(1<<5)	/* Multi-Master contention interrupt status */
+
+
+/* Interrupt mask register */
+#define TXEIM		(1<<0)	/* Transmit FIFO empty interrupt status */
+#define TXOIM		(1<<1)	/* Transmit FIFO overflow interrupt status */
+#define RXUIM		(1<<2)	/* Receive FIFO underflow interrupt status */
+#define RXOIM		(1<<3)	/* Receive FIFO overflow interrupt status */
+#define RXFIM		(1<<4)	/* Receive FIFO full interrupt status */
+#define MSTIM		(1<<5)	/* Multi-Master contention interrupt status */
+
+struct comcerto_spi
+{
+	struct spi_adapter *adapter;
+	unsigned long membase;
+	int irq;
+	unsigned long clock_rate;
+};
+
+
+#endif /* _COMCERTO_SPI_H */
diff -Naur linux-2.6.30.4-orig/drivers/spi2/busses/Kconfig linux-2.6.30.4-new/drivers/spi2/busses/Kconfig
--- linux-2.6.30.4-orig/drivers/spi2/busses/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/drivers/spi2/busses/Kconfig	2009-08-21 14:55:42.794661000 -0700
@@ -0,0 +1,9 @@
+
+menu "SPI Hardware Bus support"
+	depends on SPI_MSPD
+
+config COMCERTO_SPI
+	tristate "Comcerto"
+	depends on SPI_MSPD
+
+endmenu
diff -Naur linux-2.6.30.4-orig/drivers/spi2/busses/Makefile linux-2.6.30.4-new/drivers/spi2/busses/Makefile
--- linux-2.6.30.4-orig/drivers/spi2/busses/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/drivers/spi2/busses/Makefile	2009-08-21 14:55:42.808665000 -0700
@@ -0,0 +1 @@
+obj-$(CONFIG_COMCERTO_SPI)        += comcerto_spi.o
diff -Naur linux-2.6.30.4-orig/drivers/spi2/chips/Kconfig linux-2.6.30.4-new/drivers/spi2/chips/Kconfig
--- linux-2.6.30.4-orig/drivers/spi2/chips/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/drivers/spi2/chips/Kconfig	2009-08-21 14:55:42.825661000 -0700
@@ -0,0 +1,8 @@
+
+menu "SPI devices support"
+	depends on SPI_MSPD
+
+config SPI_SI3220
+	tristate "SI3220"
+	depends on SPI_MSPD
+endmenu
diff -Naur linux-2.6.30.4-orig/drivers/spi2/chips/Makefile linux-2.6.30.4-new/drivers/spi2/chips/Makefile
--- linux-2.6.30.4-orig/drivers/spi2/chips/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/drivers/spi2/chips/Makefile	2009-08-21 14:55:42.828662000 -0700
@@ -0,0 +1,3 @@
+obj-$(CONFIG_SPI_SI3220)        += si3220.o
+
+si3220-objs := si3220_main.o si3220_itf.o
diff -Naur linux-2.6.30.4-orig/drivers/spi2/chips/si3220.h linux-2.6.30.4-new/drivers/spi2/chips/si3220.h
--- linux-2.6.30.4-orig/drivers/spi2/chips/si3220.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/drivers/spi2/chips/si3220.h	2009-08-21 14:55:42.831662000 -0700
@@ -0,0 +1,124 @@
+/*
+ *  linux/drivers/spi2/chips/si3220.h
+ *
+ *  Copyright (C) Mindspeed Technologies
+ *
+ * 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.
+ *
+ */
+#ifndef _SI3220_H
+#define _SI3220_H
+
+#include <linux/version.h>
+#include <linux/spinlock.h>
+#include <asm/system.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/spi2/spi.h>
+#include <linux/spi2/slic_itf.h>
+
+#define SI3220_DRIVER_NAME "si3220"
+
+#define ALAW		1
+#define ULAW		2
+
+#define MAX_POTS		8
+
+#define POTS_STATE_ONHOOK	1
+#define POTS_STATE_OFFHOOK	2
+#define POTS_STATE_RINGING	3
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+typedef void irqreturn_t;
+
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define IRQ_RETVAL(x)
+#endif
+
+struct si3220_pots
+{
+	u8 state;
+	unsigned long irq_pending;
+};
+
+struct si3220_data
+{
+	struct spi_client client;
+	struct timer_list timer;
+	u8 max_pots;
+
+	struct si3220_pots pots[MAX_POTS];
+
+//	spinlock lock;
+
+	struct tasklet_struct soft_irq;
+
+	struct slic_itf *itf;
+
+	u8 irq;
+};
+
+extern struct si3220_data *si3220;
+extern struct slic_itf *si3220_itf;
+
+int si3220_start_ringing(struct si3220_data *si3220, u8 pots);
+int si3220_stop_ringing(struct si3220_data *si3220, u8 pots);
+
+void si3220_handle_state(struct si3220_data *si3220, int pots, int prev_pots_state);
+
+/* Si3220 Registers */
+#define ID		0
+#define RESET		1
+#define MSTRSTAT	3
+#define RAMSTAT		4
+#define RLYCON		5 
+#define LINEFEED	6
+#define SBIAS		8
+#define LCRRTP		9 
+#define ILIM		10
+#define CALR1		11
+#define CALR2		12
+#define IRQ0		14
+#define IRQ1		15
+#define IRQ2		16
+#define IRQ3		17
+#define IRQEN1		18
+#define IRQEN2		19
+#define IRQEN3		20
+#define RINGCON		23
+#define RINGTALO	24
+#define RINGTAHI	25
+#define RINGTILO	26
+#define RINGTIHI	27
+#define PCMMODE		53
+#define PCMTXLO		54
+#define PCMTXHI		55
+#define PCMRXLO		56
+#define PCMRXHI		57
+#define O2TALO		64
+#define RAMDATLO	101
+#define RAMDATHI	102
+#define RAMADDR		103
+
+/* Si3220 RAM */
+#define LCROFFHK	22
+#define LCRONHK		23
+#define LCRDBI		24
+#define LCRLPF		25
+
+#define RINGOF		56
+#define RINGFRHI	57
+#define RINGFRLO	58
+#define RINGAMP		59
+#define RINGPHAS	60
+
+#define RXGAIN		71
+#define TXGAIN		72
+#define DEFAULT_TXGAIN	0x4000
+#define DEFAULT_RXGAIN	0x4000
+
+
+#endif /* _SI3220_H */
diff -Naur linux-2.6.30.4-orig/drivers/spi2/chips/si3220_itf.c linux-2.6.30.4-new/drivers/spi2/chips/si3220_itf.c
--- linux-2.6.30.4-orig/drivers/spi2/chips/si3220_itf.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/drivers/spi2/chips/si3220_itf.c	2009-08-21 14:55:42.835661000 -0700
@@ -0,0 +1,178 @@
+/*
+ *  linux/drivers/spi2/chips/si3220_itf.c
+ *
+ *  Copyright (C) Mindspeed Technologies
+ *
+ * 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/spi2/slic_itf.h>
+
+#include "si3220.h"
+
+struct slic_itf *si3220_itf;
+
+/**
+* si3220_handle_state -
+*
+*/
+void si3220_handle_state(struct si3220_data *si3220, int pots, int prev_pots_state)
+{
+	if (!si3220->itf) {
+		printk (KERN_INFO "si3220 interface not registered yet pots(%d)\n", pots);
+		return;
+	}
+
+	switch (si3220->pots[pots].state) {
+	case POTS_STATE_ONHOOK:
+		si3220->itf->disconnect(si3220->itf, pots);
+		break;
+
+	case POTS_STATE_OFFHOOK:
+		if (prev_pots_state == POTS_STATE_RINGING)
+			si3220->itf->outgoing_call_ack(si3220->itf, pots);
+		else
+			si3220->itf->incoming_call(si3220->itf, pots);
+
+		break;
+
+	default:
+		break;
+	}
+}
+
+/**
+* slic_adapter_outgoing_call -
+*
+*/
+int slic_adapter_outgoing_call(struct slic_itf *itf, int pots)
+{
+	struct si3220_data *si3220 = (struct si3220_data *) itf->itf_data;
+
+	if (si3220 == NULL) {
+		printk (KERN_INFO "si3220 interface not registered yet pots(%d)\n", pots);
+		goto err;
+	}
+
+	if (pots >= si3220->max_pots) {
+		printk (KERN_INFO "pots(%d) out of range\n", pots);
+		goto err;
+	}
+
+	switch (si3220->pots[pots].state) {
+	case POTS_STATE_ONHOOK:
+		si3220->pots[pots].state = POTS_STATE_RINGING;
+		si3220_start_ringing(si3220, pots);
+		break;
+
+	case POTS_STATE_OFFHOOK:
+		goto err;
+		break;
+
+	case POTS_STATE_RINGING:
+		goto err;
+		break;
+
+	default:
+		printk (KERN_INFO "unknown pots(%d) state %d\n", pots, si3220->pots[pots].state);
+		goto err;
+		break;
+	}
+
+	return 0;
+
+  err:
+	return -1;
+}
+
+/**
+* slic_adapter_disconnect -
+*
+*/
+int slic_adapter_disconnect(struct slic_itf *itf, int pots)
+{
+	struct si3220_data *si3220 = (struct si3220_data *) itf->itf_data;
+
+	if (si3220 == NULL) {
+		printk (KERN_INFO "si3220 interface not registered yet pots(%d)\n", pots);
+		goto err;
+	}
+
+	if (pots >= si3220->max_pots) {
+		printk (KERN_INFO "pots(%d) out of range\n", pots);
+		goto err;
+	}
+
+	switch (si3220->pots[pots].state) {
+	case POTS_STATE_ONHOOK:
+		goto err;
+		break;
+
+	case POTS_STATE_OFFHOOK:
+		break;
+
+	case POTS_STATE_RINGING:
+		si3220->pots[pots].state = POTS_STATE_ONHOOK;
+		si3220_stop_ringing(si3220, pots);
+		break;
+
+	default:
+		printk (KERN_INFO "unknown pots(%d) state %d\n", pots, si3220->pots[pots].state);
+		goto err;
+		break;
+	}
+
+	return 0;
+
+  err:
+	return -1;
+}
+
+/**
+* slic_adapter_register -
+*
+*/
+int slic_adapter_register(struct slic_itf *itf)
+{
+	/* we support a single interface */
+	if (si3220_itf) {
+		printk (KERN_INFO "slic interface already registered\n");
+		goto err;
+	}
+
+	/* keep track of the registered interface */
+	si3220_itf = itf;
+
+	/* if the si3220 is already running associate it to the interface */
+	if (si3220) {
+		si3220->itf = itf;
+		si3220->itf->itf_data = (unsigned long) si3220;
+	}
+
+	return 0;
+
+  err:
+	return -1;
+}
+
+/**
+* slic_adapter_unregister -
+*
+*/
+void slic_adapter_unregister(struct slic_itf *itf)
+{
+	struct si3220_data *si3220 = (struct si3220_data *) itf->itf_data;
+	si3220_itf = NULL;
+
+	if (si3220) {
+		si3220->itf = NULL;
+		itf->itf_data = (unsigned long) NULL;
+	}
+}
+
+EXPORT_SYMBOL(slic_adapter_outgoing_call);
+EXPORT_SYMBOL(slic_adapter_disconnect);
+EXPORT_SYMBOL(slic_adapter_register);
+EXPORT_SYMBOL(slic_adapter_unregister);
diff -Naur linux-2.6.30.4-orig/drivers/spi2/chips/si3220_main.c linux-2.6.30.4-new/drivers/spi2/chips/si3220_main.c
--- linux-2.6.30.4-orig/drivers/spi2/chips/si3220_main.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/drivers/spi2/chips/si3220_main.c	2009-08-21 14:55:42.840662000 -0700
@@ -0,0 +1,897 @@
+/*
+ *  linux/drivers/spi2/chips/si3220_main.c
+ *
+ *  Copyright (C) Mindspeed Technologies
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <mach/irqs.h>
+
+#include "si3220.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
+static int law = ULAW;
+module_param(law, int, 0);
+MODULE_PARM_DESC(law, "sets TDM line encoding to u-law or A-law, i.e, 1 = A-law, 2 = u-law");
+
+static int txgain = DEFAULT_TXGAIN;
+module_param(txgain, int, 0);
+MODULE_PARM_DESC(txgain, "sets Transmit Gain Control (TXGAIN) default 0x4000");
+
+static int rxgain = DEFAULT_RXGAIN;
+module_param(rxgain, int, 0);
+MODULE_PARM_DESC(rxgain, "sets Receive Gain Control (RXGAIN) default 0x4000");
+#else
+
+static int law = ULAW;
+MODULE_PARM(law, "i");
+MODULE_PARM_DESC(law, "sets TDM line encoding to u-law or A-law, i.e, 1 = A-law, 2 = u-law");
+
+static int txgain = DEFAULT_TXGAIN;
+MODULE_PARM(txgain, "i");
+MODULE_PARM_DESC(txgain, "sets Transmit Gain Control (TXGAIN) default 0x4000");
+
+static int rxgain = DEFAULT_RXGAIN;
+MODULE_PARM(rxgain, "i");
+MODULE_PARM_DESC(rxgain, "sets Receive Gain Control (RXGAIN) default 0x4000");
+#endif
+
+
+
+extern struct spi_driver si3220_driver;
+struct si3220_data *si3220;
+
+static u16 si3220_ram_init[]=
+{
+	0x00, 0x2668,
+	0x01, 0x0592,
+	0x02, 0xf9a2,
+	0x03, 0x0197,
+	0x04, 0x0268,
+	0x05, 0x032f,
+	0x06, 0x0738,
+
+	LCROFFHK,	0x0c9c,		/* 3.1 mA */
+	LCRONHK,	0x0f22,		/* 11 mA */
+	LCRDBI,	0x0028,		/* 50 ms */
+	LCRLPF,	0x0a10,		/* 80 Hz */
+
+	0x1b, 0x08d4,
+	0x1c, 0x0a17,
+	0x1e, 0x0a08,
+	0x1f, 0x0e54,
+	0x20, 0x0d88,
+	0x21, 0x0a08,
+	0x22, 0x0a08,
+	0x23, 0x07f5,
+	0x24, 0x00cb,
+	0x25, 0x097c,
+	0x26, 0x1b94,
+	0x27, 0x0785,
+	0x28, 0x0088,
+	0x29, 0x0088,
+	0x2a, 0x0008,
+	0x34, 0x0a08,
+	0x36, 0x7ff8,
+
+	RINGOF,		0x0000,
+	RINGFRHI,	0x3F78,
+	RINGFRLO,	0x6CE8,
+	RINGAMP,	0x00FF,
+	RINGPHAS,	0x0000,
+
+	0x3d, 0x0618,
+	0x3e, 0x0012,
+	0x3f, 0x0020,
+	0x40, 0x0b40,
+	0x41, 0x0003,
+	0x42, 0x0003,
+	0x44, 0x4000,
+	0x45, 0x008a,
+	0x46, 0x00c8,
+
+	/*RX/TX gain are set from module options
+	0x47, 0x4000
+	0x48, 0x4000
+	*/
+	0x49, 0x0090,
+	0x4a, 0x0148,
+	0x4b, 0xf8b0,
+	0x4c, 0x4a68,
+	0x4d, 0xfff8,
+	0x4e, 0x00f0,
+	0x4f, 0xfa38,
+	0x50, 0x4148,
+	0x51, 0x3dc0,
+	0x52, 0x0f98,
+	0x53, 0x24d8,
+	0x54, 0x0fd0,
+	0x55, 0xfec0,
+	0x56, 0x0048,
+	0x57, 0xffb8,
+	0x58, 0xffe8,
+	0x59, 0x00d8,
+	0x5a, 0xffd8,
+	0x5b, 0xfff8,
+	0x5c, 0xd4f0,
+	0x5d, 0xeb50,
+	0x5e, 0x3d98,
+	0x5f, 0x004f,
+	0x61, 0x3c38,
+	0x62, 0x0063,
+	0x64, 0x3ce0,
+	0x65, 0x35b0,
+	0x66, 0x0100,
+	0x67, 0x01e0,
+	0x68, 0x3be0,
+	0x69, 0x1330,
+	0x6a, 0x1118,
+	0x6b, 0x1d88,
+	0x6c, 0x2ae1,
+	0x6d, 0x28f3,
+	0x6e, 0x25c2,
+	0x6f, 0x249b,
+	0x70, 0x1999,
+	0x71, 0x1013,
+	0x72, 0x1013,
+	0x73, 0x0cc5,
+	0x74, 0x0cc5,
+	0x75, 0x308c,
+	0x76, 0x1013,
+	0x77, 0x00e5,
+	0x78, 0x0a1c,
+	0xa3, 0x3858,
+	0xa4, 0x7748,
+	0xa5, 0xc7a0
+};
+
+/**
+ * msb_to_lsb_4bit -
+ *
+ */
+static u16 msb_to_lsb_4bit(u16 msb)
+{
+        u16 lsb = 0;
+        int i;
+
+        for (i = 0; i < 4; i++)
+        {
+                lsb |= ((msb >> i) & 0x1) << (3 - i);
+        }
+
+        return lsb;
+}
+
+/**
+ * read_register_8bit -
+ *
+ *
+ */
+static u8 read_register_8bit(struct spi_client *client, u8 ch, u8 reg)
+{
+	u8 val;
+
+	if (ch == 0xff)
+		ch = 0x80;
+	else
+		ch = msb_to_lsb_4bit(ch) & 0xf;
+
+	spi_writeb(client, 0x60 | ch);
+	spi_writeb(client, reg);
+	spi_readb(client, &val);
+
+	return val;
+}
+
+/**
+ * read_register_16bit -
+ *
+ *
+ */
+static u8 read_register_16bit(struct spi_client *client, u8 ch, u8 reg)
+{
+	u16 val;
+
+	if (ch == 0xff)
+		ch = 0x80;
+	else
+		ch = msb_to_lsb_4bit(ch) & 0xf;
+
+	spi_writew(client, ((0x60 | ch) << 8) | reg);
+	spi_readw(client, &val);
+
+	return (u8) (val & 0xff);
+}
+
+/**
+ * read_register_4bit -
+ *
+ *
+ */
+u8 read_register_4bit(struct spi_client *client, u8 ch, u8 reg)
+{
+	u8 buffer[6];
+	
+	if (ch == 0xff)
+		ch = 0x80;
+	else
+		ch = msb_to_lsb_4bit(ch) & 0xf;
+
+	buffer[0] = 0x6 | (ch >> 4);
+	buffer[1] = ch & 0xf;
+	buffer[2] = reg >> 4;
+	buffer[3] = reg & 0xf;
+
+	spi_write_mem(client, 4, buffer, 2);
+	spi_write_mem(client, 4, buffer + 2, 2);
+	spi_read_mem(client, 4, buffer + 4, 2);
+
+	return buffer[4] | (buffer[5] << 4);
+}
+
+/**
+ * write_register_8bit -
+ *
+ *
+ */
+static void write_register_8bit(struct spi_client *client, u8 ch, u8 reg, u8 val)
+{
+	if (ch == 0xff)
+		ch = 0x80;
+	else
+		ch = msb_to_lsb_4bit(ch) & 0xf;
+
+	spi_writeb(client, 0x20 | ch);
+	spi_writeb(client, reg);
+	spi_writeb(client, val);
+}
+
+/**
+ * write_register_16bit -
+ *
+ *
+ */
+static void write_register_16bit(struct spi_client *client, u8 ch, u8 reg, u8 val)
+{
+	if (ch == 0xff)
+		ch = 0x80;
+	else
+		ch = msb_to_lsb_4bit(ch) & 0xf;
+
+	spi_writew(client, ((0x20 | ch) << 8) | reg);
+	spi_writew(client, val | (val << 8));
+}
+
+/**
+ * write_register_4bit -
+ *
+ *
+ */
+void write_register_4bit(struct spi_client *client, u8 ch, u8 reg, u8 val)
+{
+	u8 buffer[6];
+
+	if (ch == 0xff)
+		ch = 0x80;
+	else
+		ch = msb_to_lsb_4bit(ch) & 0xf;
+
+	buffer[0] = 0x2 | (ch >> 4);
+	buffer[1] = ch & 0xf;
+	buffer[2] = reg >> 4;
+	buffer[3] = reg & 0xf;
+	buffer[4] = val >> 4;
+	buffer[5] = val & 0xf;
+
+	spi_write_mem(client, 4, buffer, 2);
+	spi_write_mem(client, 4, buffer + 2, 2);
+	spi_write_mem(client, 4, buffer + 4, 2);
+}
+
+
+/**
+ * read_ram_8bit -
+ *
+ *
+ */
+u16 read_ram_8bit(struct spi_client *client, u8 ch, u8 reg)
+{
+	u16 val;
+
+	while(read_register_8bit(client, ch, RAMSTAT) & 0x1)
+		;
+
+	write_register_8bit(client, ch, RAMADDR, reg);
+
+	while(read_register_8bit(client, ch, RAMSTAT) & 0x1)
+		;
+
+	val = (u16) read_register_8bit(client, ch, RAMDATLO);
+	val |= ((u16) read_register_8bit(client, ch, RAMDATHI)) << 8;
+
+	return val;
+}
+
+/**
+ * read_ram_16bit -
+ *
+ *
+ */
+static u16 read_ram_16bit(struct spi_client *client, u8 ch, u8 reg)
+{
+	u16 val;
+
+	while(read_register_16bit(client, ch, RAMSTAT) & 0x1)
+		;
+
+	write_register_16bit(client, ch, RAMADDR, reg);
+
+	while(read_register_16bit(client, ch, RAMSTAT) & 0x1)
+		;
+
+	val = (u16) read_register_16bit(client, ch, RAMDATLO);
+	val |= ((u16)read_register_16bit(client, ch, RAMDATHI)) << 8;
+
+	return val;
+}
+
+/**
+ * write_ram_8bit -
+ *
+ *
+ */
+void write_ram_8bit(struct spi_client *client, u8 ch, u8 reg, u16 val)
+{
+	while(read_register_8bit(client, ch, RAMSTAT) & 0x1)
+		;
+
+	write_register_8bit(client, ch, RAMDATLO, (u8)(val & 0xff));
+	write_register_8bit(client, ch, RAMDATHI, (u8)((val & 0xff00) >> 8));
+	write_register_8bit(client, ch, RAMADDR, reg);
+}
+
+/**
+ * write_ram_16bit -
+ *
+ *
+ */
+static void write_ram_16bit(struct spi_client *client, u8 ch, u8 reg, u16 val)
+{
+	while(read_register_16bit(client, ch, RAMSTAT) & 0x1)
+		;
+
+	write_register_16bit(client, ch, RAMDATLO, (u8)(val & 0xff));
+	write_register_16bit(client, ch, RAMDATHI, (u8)((val & 0xff00) >> 8));
+	write_register_16bit(client, ch, RAMADDR, reg);
+}
+
+/**
+ * si3220_start_ringing -
+ *
+ *
+ */
+int si3220_start_ringing(struct si3220_data *si3220, u8 pots)
+{
+	struct spi_client *client = &si3220->client;
+
+//	printk(KERN_INFO "si3220_start_ringing(%#lx, %d)\n", (unsigned long) si3220, pots);
+
+	write_register_8bit(client, pots, RINGCON, 0x18);
+	write_register_8bit(client, pots, LINEFEED, 0x04);
+
+	return 0;
+}
+
+/**
+ * si3220_stop_ringing -
+ *
+ *
+ */
+int si3220_stop_ringing(struct si3220_data *si3220, u8 pots)
+{
+	struct spi_client *client = &si3220->client;
+
+//	printk(KERN_INFO "si3220_stop_ringing(%#lx, %d)\n", (unsigned long) si3220, pots);
+
+	write_register_8bit(client, pots, LINEFEED, 0x01);
+	write_register_8bit(client, pots, RINGCON, 0x00);
+
+	return 0;
+}
+
+
+/**
+ * si3220_device_init -
+ *
+ *
+ */
+static int si3220_device_init(struct si3220_data *si3220, int device)
+{
+	struct spi_client *client = &si3220->client;
+	u8 ch;
+	u8 pots;
+	u8 pcmmode;
+	u8 pcm_clock_slot;
+	int i;
+
+	printk(KERN_INFO "si3220_device_init(%#lx, %d)\n", (unsigned long) si3220, device);
+
+	for (pots = 0; pots < 2; pots++) {
+		ch = device * 2 + pots;
+
+		for (i = 0; i < sizeof(si3220_ram_init) / (2 * sizeof(u16)); i++) {
+			write_ram_16bit(client, ch, si3220_ram_init[i * 2], si3220_ram_init[i * 2 + 1]);
+//			printk(KERN_INFO "%#x\n", read_ram_16bit(client, ch, si3220_ram_init[i * 2]));
+		}
+
+		for (i = 0; i < sizeof(si3220_ram_init) / (2 * sizeof(u16)); i++) {
+			if (read_ram_16bit(client, ch, si3220_ram_init[i * 2]) != si3220_ram_init[i * 2 + 1]) {
+				printk(KERN_ERR "si3220: ram write failed at addr %#x\n", si3220_ram_init[i * 2]);
+				goto err;
+			}
+		}
+
+		write_ram_16bit(client, ch, RXGAIN, rxgain);
+			printk(KERN_INFO "si3220: pots %d rxgain=0x%04x\n", pots, rxgain);
+
+		write_ram_16bit(client, ch, TXGAIN, txgain);
+			printk(KERN_INFO "si3220: pots %d txgain=0x%04x\n", pots, txgain);
+
+		/* Set PCM mode */
+		if (law == ALAW)
+		{
+			pcmmode = 0x34;
+			printk(KERN_INFO "si3220: pots %d set in A-law\n", pots);
+	
+		}
+		else
+		{
+			pcmmode = 0x31;
+			printk(KERN_INFO "si3220: pots %d set in u-law\n", pots);
+		}
+
+		pcm_clock_slot = 1 + ch * 8;
+
+		write_register_8bit(client, ch, PCMMODE, pcmmode);
+
+		write_register_8bit(client, ch, PCMTXLO, pcm_clock_slot & 0xff);
+		write_register_8bit(client, ch, PCMTXHI, (pcm_clock_slot >> 8) & 0x3);
+
+		write_register_8bit(client, ch, PCMRXLO, pcm_clock_slot & 0xff);
+		write_register_8bit(client, ch, PCMRXHI, (pcm_clock_slot >> 8) & 0x3);
+
+		/* Set Ring Configuration */
+		write_register_8bit(client, ch, RINGTALO, 0x80);
+		write_register_8bit(client, ch, RINGTAHI, 0x3E);
+		write_register_8bit(client, ch, RINGTILO, 0x80);
+		write_register_8bit(client, ch, RINGTIHI, 0x3E);
+		write_register_8bit(client, ch, RINGCON, 0x00);
+
+		/* Clear all Interrupts */
+		read_register_8bit(client, ch, IRQ0);
+		read_register_8bit(client, ch, IRQ1);
+		read_register_8bit(client, ch, IRQ2);
+		read_register_8bit(client, ch, IRQ3);
+
+		/* Enable Interrupts */
+		write_register_8bit(client, ch, IRQEN1, 0x00);		/* disable all irq1 interrupts */
+		write_register_8bit(client, ch, IRQEN2, 0x02);		/* enable loop closure interrupts for irq2 */
+		write_register_8bit(client, ch, IRQEN3, 0x00);		/* disable all irq3 interrupts */
+
+		/* Set active Mode */
+		write_register_8bit(client, ch, LINEFEED, 0x01);
+	}
+
+	return 0;
+
+  err:
+	return -1;
+}
+
+
+/**
+ * si3220_device_reset -
+ *
+ *
+ */
+static int si3220_device_reset(struct si3220_data *si3220, int device)
+{
+	struct spi_client *client = &si3220->client;
+	int count = 0;
+
+	printk(KERN_INFO "si3220: resetting device %d\n", device);
+
+	write_register_8bit(client, device * 2, RESET, 0x03);
+
+	while (read_register_8bit(client, device * 2, RESET)) {
+		set_task_state(current, TASK_UNINTERRUPTIBLE);
+		schedule_timeout((50 * HZ) / 1000);
+		set_task_state(current, TASK_RUNNING);
+
+		count ++;
+
+		if (count > 10) {
+			printk(KERN_ERR "si3220: reset failed for device %d, reset %#x\n", device, read_register_8bit(client, device * 2, RESET));
+			goto err;
+		}
+	}
+
+	printk(KERN_INFO "si3220: reset ended for device %d\n", device);
+
+	return 0;
+
+  err:
+	return -1;
+}
+
+
+/**
+ * si3220_check_mstrstat -
+ *
+ *
+ */
+static int si3220_check_mstrstat(struct si3220_data *si3220, int device)
+{
+	struct spi_client *client = &si3220->client;
+	u8 status;
+	int count = 0;
+
+	while (((status = read_register_8bit(client, device * 2, MSTRSTAT)) & 0x1c) != 0x1c) {
+		set_task_state(current, TASK_UNINTERRUPTIBLE);
+		schedule_timeout((50 * HZ) / 1000);
+		set_task_state(current, TASK_RUNNING);
+
+		count ++;
+
+		if (count > 10) {
+			printk(KERN_ERR "si3220: device %d probe failed, status %#x\n", device, status);
+			goto err;
+		}
+	}
+
+	return 0;
+
+  err:
+	return -1;
+}
+
+/**
+ * si3220_device_probe -
+ *
+ *
+ */
+static int si3220_device_probe(struct si3220_data *si3220)
+{
+	struct spi_client *client = &si3220->client;
+	u8 chip_id;
+	char name[10];
+	char rev[10];
+	int i;
+
+	printk(KERN_INFO "si3220_device_probe(%#lx)\n", (unsigned long) si3220);
+
+	for (i = 0; i < MAX_POTS / 2; i++)
+	{
+		if (si3220_check_mstrstat(si3220, i))
+			goto end;
+
+		chip_id = read_register_8bit(client, i * 2, ID);
+
+		printk(KERN_INFO "chip_id: %#x\n", chip_id);
+
+		switch ((chip_id >> 4) & 0x7) {
+		case 0:
+			strcpy (name, "si3220");
+			break;
+
+		case 2:
+			strcpy (name, "si3225");
+			break;
+		default:
+			goto end;
+		}
+
+		switch (chip_id & 0xf) {
+		case 1:
+			strcpy (rev, "rev A");
+			break;
+
+		case 2:
+			strcpy (rev, "rev B");
+			break;
+
+		case 3:
+			strcpy (rev, "rev C");
+			break;
+
+		case 4:
+			strcpy (rev, "rev D");
+			break;
+
+		case 5:
+			strcpy (rev, "rev E");
+			break;
+
+		case 6:
+			strcpy (rev, "rev F");
+			break;
+
+                case 7:
+                        strcpy (rev, "rev G");
+                        break;
+
+		default:
+			goto end;
+		}
+
+		if (si3220_device_reset(si3220, i))
+			goto end;
+
+		if (si3220_check_mstrstat(si3220, i))
+			goto end;
+		
+		if (read_register_8bit(client, i * 2, CALR1) != 0x3f ||
+		    read_register_8bit(client, i * 2, ILIM) != 0x05 ||
+		    read_register_8bit(client, i * 2, PCMMODE) != 0x05 ||
+		    read_register_8bit(client, i * 2, RLYCON) != 0xa3 ||
+		    read_register_8bit(client, i * 2, SBIAS) != 0xe0) {
+			printk(KERN_ERR "si3220: device %d, registers default value don't match", i);
+			goto end;
+		}
+		
+		printk(KERN_INFO "si3220: device %d, status %#x\n", i, read_register_8bit(client, i * 2, MSTRSTAT));
+
+		printk(KERN_INFO "found a %s device %s\n", name, rev);
+
+		if (si3220_device_init(si3220, i))
+			goto end;
+	}
+
+  end:
+	si3220->max_pots = i * 2;
+
+	return 0;
+}
+
+/**
+ * si3220_softirq_handler -
+ *
+ *
+ */
+void si3220_softirq_handler(unsigned long data)
+{
+	struct si3220_data *si3220 = (struct si3220_data *)data;
+	struct spi_client *client = &si3220->client;
+	u8 pots_state, prev_pots_state;
+	int i;
+
+//	printk (KERN_INFO "si3220_softirq_handler(%#lx)\n", (unsigned long)data);
+
+	for (i = 0; i < si3220->max_pots; i++) {
+
+		if (test_and_clear_bit(0, &si3220->pots[i].irq_pending)) {
+
+			if (read_register_8bit(client, i, LCRRTP) & 0x1)
+				pots_state = POTS_STATE_OFFHOOK;
+			else
+				pots_state = POTS_STATE_ONHOOK;
+
+			if (si3220->pots[i].state != pots_state) {
+				prev_pots_state = si3220->pots[i].state;
+				si3220->pots[i].state = pots_state;
+
+				si3220_handle_state(si3220, i, prev_pots_state);
+
+//				printk(KERN_INFO "Pots %d state %d\n", i, pots_state);
+			}
+		}
+	}
+}
+
+/**
+ * si3220_irq_handler -
+ *
+ *
+ */
+irqreturn_t si3220_irq_handler(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct si3220_data *si3220 = (struct si3220_data *)dev_id;
+	struct spi_client *client = &si3220->client;
+	int handled = 0;
+	int i;
+
+//	printk (KERN_INFO "si3220_irq_handler(%d, %#lx, %#lx)\n", irq, (unsigned long)dev_id, (unsigned long) regs);
+
+	for (i = 0; i < si3220->max_pots; i++) {
+		if (read_register_8bit(client, i, IRQ2) & 0x2) {
+			handled = 1;
+
+			set_bit(0, &si3220->pots[i].irq_pending);
+		}
+	}
+
+	if (handled) {
+		tasklet_schedule(&si3220->soft_irq);
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+static void si3220_spi_tx(unsigned long data)
+{
+        struct si3220_data *si3220 = (struct si3220_data *) data;
+
+	printk (KERN_INFO "%#x %#x %#x\n", readl(COMCERTO_GPIO_INPUT_REG), readl(COMCERTO_GPIO_LO_INT_ENABLE_REG),
+					readl(COMCERTO_GPIO_HI_INT_ENABLE_REG));
+
+	printk (KERN_INFO "%#x\n", readl(COMCERTO_INTC_STATUS_REG_1));
+
+	si3220->timer.expires = jiffies + 5 * HZ;
+        add_timer(&si3220->timer);
+}
+
+/**
+ * si3200_timer_init -
+ *
+ *
+ */
+void si3200_timer_init(struct si3220_data *si3220)
+{
+        init_timer (&si3220->timer);
+
+        si3220->timer.function = si3220_spi_tx;
+        si3220->timer.data = (unsigned long) si3220;
+
+        si3220->timer.expires = jiffies + (20 * HZ);
+        add_timer(&si3220->timer);
+}
+
+/**
+ * si3220_attach_adapter -
+ *
+ *
+ */
+int si3220_attach_adapter(struct spi_adapter *adapter)
+{
+	struct spi_client *client;
+	int i;
+
+	si3220 = kmalloc(sizeof (struct si3220_data), GFP_KERNEL);
+	if (si3220 == NULL)
+		goto err0;
+
+	memset(si3220, 0, sizeof(struct si3220_data));
+
+	/* if the si3220 interface is already registered */
+	if (si3220_itf) {
+		si3220->itf = si3220_itf;
+		si3220->itf->itf_data = (unsigned long) si3220;
+	}
+
+	si3220->irq = 32 + COMCERTO_SI3220_GPIO_IRQ;
+
+//	spin_lock_init(&si3220->lock);
+	tasklet_init(&si3220->soft_irq, si3220_softirq_handler, (unsigned long) si3220);
+
+	client = &si3220->client;
+
+	spi_set_clientdata(client, si3220);
+
+	client->adapter = adapter;
+	client->driver = &si3220_driver;
+	client->config.cs_msk = 0x1;		/* Use chip select 0 */
+	client->config.sc_polarity = 1;		/* inactive state of serial clock is high */
+	client->config.sc_phase = 1;		/* serial clock toggles at the start of first data bit */
+	client->config.sc_rate = 12000000;	/* 12 Mhz */
+	client->config.cs_delay = 8;		/* 8 bits */
+
+	if (spi_attach_client(client))
+		goto err1;
+
+	if (si3220_device_probe(si3220))
+		goto err2;
+
+	for (i = 0; i < si3220->max_pots; i++) {
+		if (read_register_8bit(client, i, LCRRTP) & 0x1)
+			si3220->pots[i].state = POTS_STATE_OFFHOOK;
+		else
+			si3220->pots[i].state = POTS_STATE_ONHOOK;
+
+
+		clear_bit(0, &si3220->pots[i].irq_pending);
+
+//		si3220_start_ringing(si3220, i);
+	}
+
+	if (request_irq(si3220->irq, si3220_irq_handler, SA_SHIRQ, SI3220_DRIVER_NAME, si3220))
+		goto err2;
+	
+//	si3200_timer_init(si3220);
+
+	return 0;
+
+  err2:
+	spi_detach_client(client);
+
+  err1:
+	kfree(si3220);
+
+  err0:
+	return -1;
+}
+
+/**
+ * si3220_detach_client -
+ *
+ *
+ */
+int si3220_detach_client(struct spi_client *client)
+{
+	struct si3220_data *si3220 = spi_get_clientdata(client);
+	int i;
+
+	free_irq(si3220->irq, si3220);
+
+	for (i = 0; i < si3220->max_pots / 2; i++)
+		si3220_device_reset(si3220, i);
+
+//	del_timer(&si3220->timer);
+
+
+	/* if the si3220 interface has been registered */
+	if (si3220->itf) {
+		si3220->itf->itf_data = (unsigned long) NULL;
+		si3220->itf = NULL;
+	}
+
+	spi_detach_client(client);
+
+	kfree(si3220);
+
+	return 0;
+}
+
+struct spi_driver si3220_driver = {
+	.name = SI3220_DRIVER_NAME,
+	.attach_adapter = si3220_attach_adapter,
+	.detach_client	= si3220_detach_client,
+};
+
+
+/**
+ * si3220_init -
+ *
+ *
+ */
+static int __init si3220_init(void)
+{
+	spi_add_driver(&si3220_driver);
+
+	return 0;
+}
+
+/**
+ * si3220_exit -
+ *
+ *
+ */
+static void __exit si3220_exit(void)
+{
+	spi_del_driver(&si3220_driver);
+}
+
+MODULE_AUTHOR("Rui Sousa <rui.sousa@mindspeed.com>");
+MODULE_DESCRIPTION("SI3220 SPI device driver");
+MODULE_LICENSE("GPL");
+
+module_init(si3220_init);
+module_exit(si3220_exit);
diff -Naur linux-2.6.30.4-orig/drivers/spi2/Kconfig linux-2.6.30.4-new/drivers/spi2/Kconfig
--- linux-2.6.30.4-orig/drivers/spi2/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/drivers/spi2/Kconfig	2009-08-21 14:55:42.843662000 -0700
@@ -0,0 +1,16 @@
+#
+# Character device configuration
+#
+
+menu "SPI2 support"
+	depends on (ARCH_M821XX || ARCH_M822XX || ARCH_M825XX2 || ARCH_M828XX)
+config SPI_MSPD
+	tristate "Mindspeed SPI support"
+	help
+	  Mindspeed SPI (Serial Pheripheral Interface bus) core support
+
+source drivers/spi2/busses/Kconfig
+source drivers/spi2/chips/Kconfig
+
+endmenu
+
diff -Naur linux-2.6.30.4-orig/drivers/spi2/Makefile linux-2.6.30.4-new/drivers/spi2/Makefile
--- linux-2.6.30.4-orig/drivers/spi2/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/drivers/spi2/Makefile	2009-08-21 14:55:42.846663000 -0700
@@ -0,0 +1,7 @@
+#
+# Makefile for the spi core.
+#
+
+obj-$(CONFIG_SPI_MSPD) += spi-core.o
+obj-y		  += busses/ chips/
+
diff -Naur linux-2.6.30.4-orig/drivers/spi2/spi-core.c linux-2.6.30.4-new/drivers/spi2/spi-core.c
--- linux-2.6.30.4-orig/drivers/spi2/spi-core.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/drivers/spi2/spi-core.c	2009-08-21 14:55:42.848663000 -0700
@@ -0,0 +1,507 @@
+/*
+ *  linux/drivers/spi2/spi-core.c
+ *
+ *  Copyright (C) 2006 Mindspeed Technologies, Inc.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+ 
+
+#if !defined (AUTOCONF_INCLUDED)
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+#include <asm/delay.h>
+
+#include <linux/spi2/spi.h>
+
+static LIST_HEAD(spi_adapters);
+static LIST_HEAD(spi_drivers);
+
+/**
+ * spi_write_mem -
+ *
+ *
+ */
+int spi_write_mem(struct spi_client *client, u8 fs, u8 *buffer, int len)
+{
+	struct spi_transfer transfer;
+	struct spi_adapter *adapter = client->adapter;
+	unsigned long flags;
+	int rc;
+
+	memset(&transfer, 0, sizeof (struct spi_transfer));
+
+	transfer.fs = fs;
+	transfer.mode = SPI_TRANSFER_MODE_WRITE_ONLY;
+	transfer.wbuf = buffer;
+	transfer.wlen = len;
+
+	spin_lock_irqsave(&adapter->lock, flags);
+	rc = adapter->do_transfer(adapter, &transfer, &client->config);
+	spin_unlock_irqrestore(&adapter->lock, flags);
+
+	/* deassert the chip select at least for this long */
+	udelay (1 + ((1000000 * client->config.cs_delay) / client->config.sc_rate));
+
+	return rc;
+}
+
+
+/**
+ * spi_write_single -
+ *
+ *
+ */
+int spi_write_single(struct spi_client *client, u8 fs, u16 value)
+{
+	return spi_write_mem(client, fs, (u8 *)&value, 1);
+}
+
+/**
+ * spi_writen -
+ *
+ *
+ */
+int spi_writen(struct spi_client *client, u4 value)
+{
+	return spi_write_mem(client, 4, (u8 *)&value, 1);
+}
+
+
+/**
+ * spi_writeb -
+ *
+ *
+ */
+int spi_writeb(struct spi_client *client, u8 value)
+{
+	return spi_write_mem(client, 8, &value, 1);
+}
+
+
+/**
+ * spi_writew -
+ *
+ *
+ */
+int spi_writew(struct spi_client *client, u16 value)
+{
+	return spi_write_mem(client, 16, (u8 *)&value, 1);
+}
+
+/**
+ * spi_read_mem -
+ *
+ *
+ */
+int spi_read_mem(struct spi_client *client, u8 fs, u8 *buffer, int len)
+{
+	struct spi_transfer transfer;
+	struct spi_adapter *adapter = client->adapter;
+	unsigned long flags;
+	int rc;
+
+	memset(&transfer, 0, sizeof (struct spi_transfer));
+
+	transfer.fs = fs;
+	transfer.mode = SPI_TRANSFER_MODE_READ_ONLY;
+	transfer.rbuf = buffer;
+	transfer.rlen = len;
+
+	spin_lock_irqsave(&adapter->lock, flags);
+	rc = adapter->do_transfer(adapter, &transfer, &client->config);
+	spin_unlock_irqrestore(&adapter->lock, flags);
+
+	/* deassert the chip select at least for this long */
+	udelay (1 + ((1000000 * client->config.cs_delay) / client->config.sc_rate));
+
+	return rc;
+}
+
+
+/**
+ * spi_read_single -
+ *
+ *
+ */
+int spi_read_single(struct spi_client *client, u8 fs, u16 *value)
+{
+	return spi_read_mem(client, fs, (u8 *) value, 1);
+}
+
+/**
+ * spi_readn -
+ *
+ *
+ */
+int spi_readn(struct spi_client *client, u4 *value)
+{
+	return spi_read_mem(client, 4, (u8 *)value, 1);
+}
+
+
+/**
+ * spi_readb -
+ *
+ *
+ */
+int spi_readb(struct spi_client *client, u8 *value)
+{
+	return spi_read_mem(client, 8, value, 1);
+}
+
+
+/**
+ * spi_readw -
+ *
+ *
+ */
+int spi_readw(struct spi_client *client, u16 *value)
+{
+	return spi_read_mem(client, 16, (u8 *)value, 1);
+}
+
+
+/**
+ * spi_read_mem -
+ *
+ *
+ */
+int spi_writeread_mem(struct spi_client *client, u8 fs, u8 *rbuffer, int rlen, u8 *wbuffer, int wlen)
+{
+	struct spi_transfer transfer;
+	struct spi_adapter *adapter = client->adapter;
+	unsigned long flags;
+	int rc;
+
+	memset(&transfer, 0, sizeof (struct spi_transfer));
+
+	transfer.fs = fs;
+	transfer.mode = SPI_TRANSFER_MODE_WRITE_READ;
+	transfer.rbuf = rbuffer;
+	transfer.rlen = rlen;
+	transfer.wbuf = wbuffer;
+	transfer.wlen = wlen;
+
+	spin_lock_irqsave(&adapter->lock, flags);
+	rc = adapter->do_transfer(adapter, &transfer, &client->config);
+	spin_unlock_irqrestore(&adapter->lock, flags);
+
+	/* deassert the chip select at least for this long */
+	udelay (1 + ((1000000 * client->config.cs_delay) / client->config.sc_rate));
+
+	return rc;
+}
+/**
+ * spi_add_adapter -
+ *
+ *
+ */
+int spi_add_adapter(struct spi_adapter *adapter)
+{
+	struct spi_driver *driver;
+	struct list_head *item;
+
+	printk(KERN_INFO "SPI core: add adapter %s\n", adapter->name);
+
+#ifdef _USE_DRIVER_MODEL_
+	sprintf(adapter->dev.bus_id, "spi-%d", adapter->nr);
+	adapter->dev.driver = &spi_adapter_driver;
+	adapter->dev.release = &spi_adapter_dev_release;
+	device_register(&adapter->dev);
+#endif
+
+	list_add(&adapter->list, &spi_adapters);
+	INIT_LIST_HEAD(&adapter->clients);
+
+	adapter->lock = SPIN_LOCK_UNLOCKED;
+
+	list_for_each(item, &spi_drivers) {
+		driver = list_entry(item, struct spi_driver, list);
+
+		/* We ignore the return code; if it fails, too bad */
+		driver->attach_adapter(adapter);
+	}
+
+	return 0;
+}
+
+/**
+ * spi_del_adapter -
+ *
+ *
+ */
+int spi_del_adapter(struct spi_adapter *adapter)
+{
+	struct spi_client *client;
+	struct list_head *item, *_n;
+
+	printk(KERN_INFO "SPI core: del adapter %s\n", adapter->name);
+
+	list_for_each_safe(item, _n, &adapter->clients) {
+		client = list_entry(item, struct spi_client, list);
+
+		if (client->driver->detach_client(client))
+			goto out;
+	}
+
+	list_del(&adapter->list);
+
+#ifdef _USE_DRIVER_MODEL_
+	device_unregister(&adapter->dev);
+#endif
+
+      out:
+	return 0;
+}
+
+/**
+ * spi_attach_client -
+ *
+ *
+ */
+int spi_attach_client(struct spi_client *client)
+{
+	struct spi_adapter *adapter = client->adapter;
+	struct spi_client_config *config = &client->config;
+	struct spi_adapter_caps *caps = &adapter->caps;
+
+	printk(KERN_INFO "SPI core: attach client to adapter %s\n", client->adapter->name);
+
+	if ((config->sc_rate >= caps->max_sc_rate) || (config->sc_rate < caps->min_sc_rate)) {
+		printk(KERN_INFO "SPI core: client serial clock rate %ld out of range [%ld, %ld]", config->sc_rate,
+		       caps->min_sc_rate, caps->max_sc_rate);
+
+		goto err;
+	}
+
+	if (config->cs_msk & ~caps->cs_msk) {
+		printk(KERN_INFO "SPI core: client cs mask %#x not supported %#x", config->cs_msk,
+		       caps->cs_msk);
+
+		goto err;
+	}
+
+#ifdef _USE_DRIVER_MODEL_
+	device_register(&client->device);
+#endif
+	list_add(&client->list, &adapter->clients);
+
+	return 0;
+
+      err:
+	return -1;
+}
+
+/**
+ * spi_dettach_client -
+ *
+ *
+ */
+int spi_detach_client(struct spi_client *client)
+{
+	printk(KERN_INFO "SPI core: client detach from adapter %s\n", client->adapter->name);
+
+	list_del(&client->list);
+
+#ifdef _USE_DRIVER_MODEL_
+	device_unregister(&client->dev);
+#endif
+	return 0;
+}
+
+/**
+ * spi_add_driver -
+ *
+ *
+ */
+int spi_add_driver(struct spi_driver *driver)
+{
+	struct spi_adapter *adapter;
+	struct list_head *item;
+
+	printk(KERN_INFO "SPI core: add driver %s\n", driver->name);
+
+#ifdef _USE_DRIVER_MODEL_
+	driver->driver.name = driver->name;
+	driver->driver.bus = &spi_bus_type;
+	driver->driver.probe = spi_device_probe;
+	driver->driver.remove = spi_device_remove;
+
+	if (driver_register(&driver->driver))
+		goto err;
+#endif
+	list_add(&driver->list, &spi_drivers);
+
+	list_for_each(item, &spi_adapters) {
+		adapter = list_entry(item, struct spi_adapter, list);
+		driver->attach_adapter(adapter);
+	}
+
+	return 0;
+
+#ifdef _USE_DRIVER_MODEL_
+      err:
+	return -1;
+#endif
+}
+
+/**
+ * spi_del_driver -
+ *
+ *
+ */
+int spi_del_driver(struct spi_driver *driver)
+{
+	struct list_head *item1, *item2, *_n;
+	struct spi_adapter *adapter;
+	struct spi_client *client;
+
+	printk(KERN_INFO "SPI core: delete driver %s\n", driver->name);
+
+	list_for_each(item1, &spi_adapters) {
+		adapter = list_entry(item1, struct spi_adapter, list);
+
+		list_for_each_safe(item2, _n, &adapter->clients) {
+			client = list_entry(item2, struct spi_client, list);
+			if (client->driver != driver)
+				continue;
+
+			if (driver->detach_client(client))
+				goto err;
+		}
+	}
+#ifdef _USE_DRIVER_MODEL_
+	driver_unregister(&driver->driver);
+#endif
+	list_del(&driver->list);
+
+	return 0;
+
+      err:
+	return -1;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+/* match always succeeds, as we want the probe() to tell if we really accept this match */
+static int spi_device_match(struct device *dev, struct device_driver *drv)
+{
+	return 1;
+}
+
+struct bus_type spi_bus_type = {
+	.name = "spi",
+	.match = spi_device_match,
+};
+
+/**
+ * spi_driver_init -
+ *
+ *
+ */
+static int __init spi_driver_init(void)
+{
+	int retval;
+
+	printk(KERN_INFO "SPI core: loaded version 0.2\n");
+
+	retval = bus_register(&spi_bus_type);
+	if (retval)
+		goto err0;
+
+#ifdef _USE_DRIVER_MODEL_
+	retval = driver_register(&spi_driver);
+	if (retval)
+		goto err1;
+#endif
+
+	return 0;
+
+#ifdef _USE_DRIVER_MODEL_
+  err1:
+	bus_unregister(&spi_bus_type);
+#endif
+
+  err0:
+	return retval;
+}
+
+/**
+ * spi_driver_exit -
+ *
+ *
+ */
+static void __exit spi_driver_exit(void)
+{
+#ifdef _USE_DRIVER_MODEL_
+	driver_unregister(&spi_driver);
+#endif
+	bus_unregister(&spi_bus_type);
+}
+
+subsys_initcall(spi_driver_init);
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */
+
+static int __init spi_driver_init(void)
+{
+	printk(KERN_INFO "SPI core: loaded version 0.2\n");
+
+	return 0;
+}
+
+static void __exit spi_driver_exit(void)
+{
+
+}
+
+module_init(spi_driver_init);
+
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */
+
+module_exit(spi_driver_exit);
+
+EXPORT_SYMBOL(spi_add_driver);
+EXPORT_SYMBOL(spi_del_driver);
+
+EXPORT_SYMBOL(spi_add_adapter);
+EXPORT_SYMBOL(spi_del_adapter);
+
+EXPORT_SYMBOL(spi_attach_client);
+EXPORT_SYMBOL(spi_detach_client);
+
+EXPORT_SYMBOL(spi_write_single);
+EXPORT_SYMBOL(spi_write_mem);
+
+EXPORT_SYMBOL(spi_writen);
+EXPORT_SYMBOL(spi_writeb);
+EXPORT_SYMBOL(spi_writew);
+
+EXPORT_SYMBOL(spi_read_mem);
+EXPORT_SYMBOL(spi_read_single);
+EXPORT_SYMBOL(spi_writeread_mem);
+
+EXPORT_SYMBOL(spi_readn);
+EXPORT_SYMBOL(spi_readb);
+EXPORT_SYMBOL(spi_readw);
+
+MODULE_AUTHOR("Rui Sousa <rui.sousa@mindspeed.com>");
+MODULE_DESCRIPTION("SPI core");
+MODULE_LICENSE("GPL");
diff -Naur linux-2.6.30.4-orig/include/linux/spi2/slic_itf.h linux-2.6.30.4-new/include/linux/spi2/slic_itf.h
--- linux-2.6.30.4-orig/include/linux/spi2/slic_itf.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/include/linux/spi2/slic_itf.h	2009-08-21 14:55:42.856665000 -0700
@@ -0,0 +1,34 @@
+/*
+ *  linux/include/linux/slic_itf.h
+ *
+ *  Copyright (C) Mindspeed Technologies
+ *
+ * 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.
+ *
+ */
+#ifndef _SLIC_ITF_H
+#define _SLIC_ITF_H
+
+#include <linux/types.h>
+
+struct slic_itf
+{
+	void (*disconnect)(struct slic_itf *itf, u8 pots);
+	void (*incoming_call)(struct slic_itf *itf, u8 pots);
+	void (*outgoing_call_ack)(struct slic_itf *itf, u8 pots);
+	void (*flash)(struct slic_itf *itf, u8 pots);
+        void (*fxo_incoming_call)( u8 pots,int state);
+
+	unsigned long data;
+
+	unsigned long itf_data;
+};
+
+int slic_adapter_outgoing_call(struct slic_itf *itf, int pots);
+int slic_adapter_disconnect(struct slic_itf *itf, int pots);
+int slic_adapter_register(struct slic_itf *itf);
+void slic_adapter_unregister(struct slic_itf *itf);
+
+#endif /* _SLIC_ITF_H */
diff -Naur linux-2.6.30.4-orig/include/linux/spi2/spi.h linux-2.6.30.4-new/include/linux/spi2/spi.h
--- linux-2.6.30.4-orig/include/linux/spi2/spi.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.30.4-new/include/linux/spi2/spi.h	2009-08-21 14:55:42.859662000 -0700
@@ -0,0 +1,158 @@
+/*
+ *  linux/include/linux/spi.h
+ *
+ *  Copyright (C) Mindspeed Technologies
+ *
+ * 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.
+ *
+ */
+#ifndef _SPI_H
+#define _SPI_H
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#include <linux/device.h>
+#endif
+
+#define SPI_TRANSFER_MODE_WRITE_ONLY	0x01
+#define SPI_TRANSFER_MODE_READ_ONLY	0x02
+#define SPI_TRANSFER_MODE_WRITE_READ	0x03
+
+typedef u8	u4;
+
+struct spi_transfer
+{
+	u8 *wbuf;
+	unsigned int wlen;
+
+	u8 *rbuf;
+	unsigned int rlen;
+
+	u8 mode;
+
+	u8 fs;				/* transfer frame size (in bits) */
+};
+
+struct spi_adapter_caps
+{
+	unsigned long min_sc_rate;	/* maximum supported serial clock rate (in MHz) */
+	unsigned long max_sc_rate;	/* minimum supported serial clock rate (in MHz) */
+
+	u8 max_fs;			/* maximum supported frame size (in bits) */
+	u8 min_fs;			/* minimum supported frame size (in bits) */
+
+	u16 cs_msk;			/* mask of supported chip selects */
+
+	u16 max_nframe;			/* maximum supported transfer frame number */
+	u16 min_nframe;			/* minimum supported transfer frame number */
+};
+
+struct spi_client_config
+{
+	u16 cs_msk;			/* chip select mask for this client */
+	u8 sc_polarity;			/* serial clock polarity */
+	u8 sc_phase;			/* serial clock phase */
+	unsigned long sc_rate;		/* serial clock rate (in MHz)*/
+	u8 cs_delay;			/* chip select deassert time (in serial clock cycles) */
+};
+
+/* A SPI bus adapter instance */
+struct spi_adapter
+{
+	char *name;
+
+	int (*do_transfer)(struct spi_adapter *adapter, struct spi_transfer *transfer, struct spi_client_config *config);
+
+	void *data;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	struct device dev;
+#endif
+	struct list_head list;
+	struct list_head clients;
+
+	struct spi_adapter_caps caps;
+
+	spinlock_t lock;
+};
+
+/* A SPI device instance */
+struct spi_client
+{
+	struct spi_client_config config;
+
+	struct spi_driver *driver;
+
+	struct spi_adapter *adapter;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	struct device dev;
+#else
+	void *data;
+#endif
+	struct list_head list;
+};
+
+/* A SPI device driver */
+struct spi_driver {
+	char *name;
+
+	int (*attach_adapter)(struct spi_adapter *adapter);
+	int (*detach_client)(struct spi_client *client);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	struct device_driver driver;
+#endif
+	struct list_head list;
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+static inline void *spi_get_clientdata (struct spi_client *dev)
+{
+	return dev_get_drvdata (&dev->dev);
+}
+
+static inline void spi_set_clientdata (struct spi_client *dev, void *data)
+{
+	dev_set_drvdata (&dev->dev, data); 
+}
+#else
+static inline void *spi_get_clientdata (struct spi_client *dev)
+{
+	return (void *) dev->data;
+}
+
+static inline void spi_set_clientdata (struct spi_client *dev, void *data)
+{
+	dev->data = data;
+}
+#endif
+
+int spi_add_adapter(struct spi_adapter *adapter);
+int spi_del_adapter(struct spi_adapter *adapter);
+
+int spi_add_driver(struct spi_driver *driver);
+int spi_del_driver(struct spi_driver *driver);
+
+int spi_attach_client(struct spi_client *client);
+int spi_detach_client(struct spi_client *client);
+
+int spi_write_mem(struct spi_client *client, u8 fs, u8 *buffer, int len);
+int spi_write_single(struct spi_client *client, u8 fs, u16 value);
+
+int spi_writen(struct spi_client *client, u4 value);
+int spi_writeb(struct spi_client *client, u8 value);
+int spi_writew(struct spi_client *client, u16 value);
+
+int spi_read_mem(struct spi_client *client, u8 fs, u8 *buffer, int len);
+int spi_read_single(struct spi_client *client, u8 fs, u16 *value);
+int spi_writeread_mem(struct spi_client *client, u8 fs, u8 *rbuffer, int rlen, u8 *wbuffer, int wlen);
+
+int spi_readn(struct spi_client *client, u4 *value);
+int spi_readb(struct spi_client *client, u8 *value);
+int spi_readw(struct spi_client *client, u16 *value);
+
+#endif /* _SPI_H */
