diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 4268595..2db2a5c 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -38,6 +38,7 @@ COBJS-$(CONFIG_BFIN_SPI6XX) += bfin_spi6xx.o
 COBJS-$(CONFIG_CF_SPI) += cf_spi.o
 COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o
 COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
+COBJS-$(CONFIG_FTSSP010_SPI) += ftssp010_spi.o
 COBJS-$(CONFIG_EXYNOS_SPI) += exynos_spi.o
 COBJS-$(CONFIG_ICH_SPI) +=  ich.o
 COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
diff --git a/drivers/spi/ftssp010_spi.c b/drivers/spi/ftssp010_spi.c
new file mode 100644
index 0000000..fbd3601
--- /dev/null
+++ b/drivers/spi/ftssp010_spi.c
@@ -0,0 +1,333 @@
+/*
+ * Faraday Multi-function Controller - SPI Mode
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is released under the terms of GPL v2 and any later version.
+ * See the file COPYING in the root directory of the source tree for details.
+ */
+
+#include <common.h>
+#include <spi.h>
+#include <malloc.h>
+
+#include "ftssp010_spi.h"
+
+struct ftssp010_chip {
+	uint32_t fifo;
+	uint32_t rev;
+	uint32_t bus;
+	uint32_t div;
+	uint32_t mode;
+	uint32_t iobase;
+	struct {
+		uint32_t iobase;
+		uint32_t pin;
+	} gpio;
+};
+
+static struct ftssp010_chip ftssp010_info[] = {
+#if defined(CONFIG_FTSSP010_BASE) || defined(CONFIG_FTSSP010_BASE0)
+	{
+		.bus    = 0,
+		.div    = 0,
+		.mode   = 0,
+		.iobase = CONFIG_FTSSP010_BASE,
+#ifdef CONFIG_FTSSP010_GPIO_BASE
+		.gpio = { CONFIG_FTSSP010_GPIO_BASE, CONFIG_FTSSP010_GPIO_PIN },
+#endif
+	},
+#endif
+#ifdef CONFIG_FTSSP010_BASE1
+	{
+		.bus    = 1,
+		.div    = 0,
+		.mode   = 0,
+		.iobase = CONFIG_FTSSP010_BASE1,
+	},
+#endif
+#ifdef CONFIG_FTSSP010_BASE2
+	{
+		.bus    = 2,
+		.div    = 0,
+		.mode   = 0,
+		.iobase = CONFIG_FTSSP010_BASE2,
+	},
+#endif
+#ifdef CONFIG_FTSSP010_BASE3
+	{
+		.bus    = 3,
+		.div    = 0,
+		.mode   = 0,
+		.iobase = CONFIG_FTSSP010_BASE3,
+	},
+#endif
+};
+
+static struct ftssp010_chip *priv = ftssp010_info;
+
+/* Register access macros */
+#define SPI_REG32(priv, off)	\
+	*(volatile uint32_t *)((priv)->iobase + (off))
+
+#ifdef CONFIG_FTSSP010_GPIO_BASE
+#define GPIO_REG32(priv, off)	\
+	*(volatile uint32_t *)((priv)->gpio.iobase + (off))
+#endif
+
+static int ftssp010_spi_work_transfer_v1_19(const void *tx_buf, void *rx_buf,
+	int len, unsigned int flags)
+{
+	const uint8_t *txb = tx_buf;
+	uint8_t       *rxb = rx_buf;
+	uint32_t      tmp;
+	uint8_t       *p8 = (uint8_t *)&tmp;
+
+	while (len > 0) {
+		int i, depth = min(priv->fifo >> 2, len);
+		uint32_t xmsk = 0;
+
+		if (tx_buf) {
+			for (i = 0; i < depth; ++i) {
+				while (!(SPI_REG32(priv, REG_SR) & SR_TFNF))
+					;
+				tmp = 0;
+				p8[0] = *txb++;
+				SPI_REG32(priv, REG_DR) = tmp;
+			}
+			xmsk |= CR2_TXEN | CR2_TXDOE;
+			if ((SPI_REG32(priv, REG_CR2) & xmsk) != xmsk)
+				SPI_REG32(priv, REG_CR2) |= xmsk;
+
+		}
+		if (rx_buf) {
+			xmsk |= CR2_RXEN;
+			if ((SPI_REG32(priv, REG_CR2) & xmsk) != xmsk)
+				SPI_REG32(priv, REG_CR2) |= xmsk;
+			for (i = 0; i < depth; ++i) {
+				while (!SR_RFVE(SPI_REG32(priv, REG_SR)))
+					;
+				tmp = SPI_REG32(priv, REG_DR);
+				*rxb++ = p8[0];
+			}
+		}
+
+		len -= depth;
+	}
+
+	return 0;
+}
+
+static int ftssp010_spi_work_transfer(const void *tx_buf, void *rx_buf,
+	int len, unsigned int flags)
+{
+	const uint8_t *txb = tx_buf;
+	uint8_t       *rxb = rx_buf;
+	uint32_t      tmp;
+	uint8_t       *p8 = (uint8_t *)&tmp;
+
+	while (len > 0) {
+		int i, depth = min(priv->fifo >> 2, len);
+
+		for (i = 0; i < depth; ++i) {
+			while (!(SPI_REG32(priv, REG_SR) & SR_TFNF))
+				;
+			tmp = 0;
+			if (txb)
+				p8[0] = *txb++;
+			SPI_REG32(priv, REG_DR) = tmp;
+		}
+
+		for (i = 0; i < depth; ++i) {
+			while (!SR_RFVE(SPI_REG32(priv, REG_SR)))
+				;
+			tmp = SPI_REG32(priv, REG_DR);
+			if (rxb)
+				*rxb++ = p8[0];
+		}
+
+		len -= depth;
+	}
+
+	return 0;
+}
+
+/*=====================================================================*/
+/*                         Public Functions                            */
+/*=====================================================================*/
+
+/*-----------------------------------------------------------------------
+ * Determine if a SPI chipselect is valid.
+ * This function is provided by the board if the low-level SPI driver
+ * needs it to determine if a given chipselect is actually valid.
+ *
+ * Returns: 1 if bus:cs identifies a valid chip on this board, 0
+ * otherwise.
+ */
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+	uint32_t txfifo, rxfifo;
+
+	if (bus >= ARRAY_SIZE(ftssp010_info))
+		return 0;
+
+	priv = ftssp010_info + bus;
+	priv->rev = SPI_REG32(priv, REG_REVR);
+	txfifo = FEAR_TXFIFO(SPI_REG32(priv, REG_FEAR));
+	rxfifo = FEAR_RXFIFO(SPI_REG32(priv, REG_FEAR));
+	priv->fifo = min(txfifo, rxfifo);
+
+	printf("ftssp010: rev.=0x%08X, fifo=%d\n", priv->rev, priv->fifo);
+
+	if (priv->rev >= 0x00011900) {
+		if (cs > 3)
+			return 0;
+	} else {
+#ifdef CONFIG_FTSSP010_GPIO_BASE
+		if (cs > 0)
+			return 0;
+		/* setup gpio pin as an output pin */
+		GPIO_REG32(priv, 0x08) |= (1 << priv->gpio.pin);
+#else
+		return 0;
+#endif
+	}
+
+	return 1;
+}
+
+/*-----------------------------------------------------------------------
+ * Activate a SPI chipselect.
+ * This function is provided by the board code when using a driver
+ * that can't control its chipselects automatically (e.g.
+ * common/soft_spi.c). When called, it should activate the chip select
+ * to the device identified by "slave".
+ */
+void spi_cs_activate(struct spi_slave *slave)
+{
+	priv = ftssp010_info + slave->bus;
+	/* cs pull low */
+	if (priv->rev >= 0x00011900) {
+		SPI_REG32(priv, REG_CR2) = (slave->cs << 10)
+			| CR2_SSPEN | CR2_TXFCLR | CR2_RXFCLR;
+	} else {
+#ifdef CONFIG_FTSSP010_GPIO_BASE
+		GPIO_REG32(priv, 0x14) |= (1 << priv->gpio.pin);
+#endif
+	}
+	udelay_masked(1);
+}
+
+/*-----------------------------------------------------------------------
+ * Deactivate a SPI chipselect.
+ * This function is provided by the board code when using a driver
+ * that can't control its chipselects automatically (e.g.
+ * common/soft_spi.c). When called, it should deactivate the chip
+ * select to the device identified by "slave".
+ */
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+	priv = ftssp010_info + slave->bus;
+
+	/* wait until device idle */
+	while (SPI_REG32(priv, REG_SR) & SR_BUSY)
+		;
+
+	/* cs pull high */
+	if (priv->rev >= 0x00011900) {
+		SPI_REG32(priv, REG_CR2) = (slave->cs << 10) | CR2_FS;
+	} else {
+#ifdef CONFIG_FTSSP010_GPIO_BASE
+		GPIO_REG32(priv, 0x10) |= (1 << priv->gpio.pin);
+#endif
+	}
+	udelay_masked(1);
+}
+
+void spi_init(void)
+{
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+								  unsigned int max_hz, unsigned int mode) {
+	uint32_t div = 1;
+#ifdef CONFIG_FTSSP010_SCLK
+	uint32_t clk = CONFIG_FTSSP010_SCLK;
+#else
+	uint32_t clk = clk_get_rate("SSP");
+#endif
+	struct spi_slave *ss;
+
+	if (!spi_cs_is_valid(bus, cs))
+		return NULL;
+
+	ss = malloc(sizeof(struct spi_slave));
+	if (!ss)
+		return NULL;
+
+	ss->bus = bus;
+	ss->cs  = cs;
+
+	if (max_hz > 0) {
+		for (div = 0; div < 0xFFFF; ++div) {
+			if ((clk / (2 * (div + 1))) <= max_hz)
+				break;
+		}
+	}
+
+	priv = ftssp010_info + bus;
+	priv->div  = div;
+	priv->mode = mode;
+
+	printf("ftssp010: bus=%d, hz=%u\n", priv->bus, (clk / (2 * (div + 1))));
+
+	return ss;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+	SPI_REG32(priv, REG_CR1) = CR1_SDL(8) | CR1_CLKDIV(priv->div);
+
+	if (priv->rev >= 0x00011900) {
+		SPI_REG32(priv, REG_CR0) = CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO
+			| CR0_FLASH;
+		SPI_REG32(priv, REG_CR2) = CR2_TXFCLR | CR2_RXFCLR;
+	} else {
+		SPI_REG32(priv, REG_CR0) = CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO;
+		SPI_REG32(priv, REG_CR2) = CR2_TXFCLR | CR2_RXFCLR
+			| CR2_SSPEN | CR2_TXDOE;
+	}
+
+	spi_cs_deactivate(slave);
+
+	return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+	SPI_REG32(priv, REG_CR2) = 0;
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+			 const void *dout, void *din, unsigned long flags)
+{
+	u32 len = bitlen >> 3;
+
+	if (flags & SPI_XFER_BEGIN)
+		spi_cs_activate(slave);
+
+	if (priv->rev >= 0x00011900)
+		ftssp010_spi_work_transfer_v1_19(dout, din, len, flags);
+	else
+		ftssp010_spi_work_transfer(dout, din, len, flags);
+
+	if (flags & SPI_XFER_END)
+		spi_cs_deactivate(slave);
+
+	return 0;
+}
diff --git a/drivers/spi/ftssp010_spi.h b/drivers/spi/ftssp010_spi.h
new file mode 100644
index 0000000..9e33bdd
--- /dev/null
+++ b/drivers/spi/ftssp010_spi.h
@@ -0,0 +1,85 @@
+/*
+ * Faraday Multi-function Controller - SPI Mode
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is released under the terms of GPL v2 and any later version.
+ * See the file COPYING in the root directory of the source tree for details.
+ */
+
+#ifndef __FTSSP010_H
+#define __FTSSP010_H
+
+/* FTSSP010 HW Registers */
+#define REG_CR0		0x00	/* control register */
+#define REG_CR1		0x04	/* control register */
+#define REG_CR2		0x08	/* control register */
+#define REG_SR		0x0C	/* status register */
+#define REG_ICR		0x10	/* interrupt control register */
+#define REG_ISR		0x14	/* interrupt status register */
+#define REG_DR		0x18	/* data register */
+#define REG_REVR	0x60	/* revision register */
+#define REG_FEAR	0x64	/* feature register */
+
+/* Control register 0  */
+#define CR0_FFMT_MASK       (7 << 12)
+#define CR0_FFMT_SSP        (0 << 12)
+#define CR0_FFMT_SPI        (1 << 12)
+#define CR0_FFMT_MICROWIRE  (2 << 12)
+#define CR0_FFMT_I2S        (3 << 12)
+#define CR0_FFMT_AC97       (4 << 12)
+#define CR0_FLASH           (1 << 11)
+#define CR0_FSDIST(x)       (((x) & 0x03) << 8)
+#define CR0_LBM             (1 << 7)  /* Loopback mode */
+#define CR0_LSB             (1 << 6)  /* LSB first */
+#define CR0_FSPO            (1 << 5)  /* Frame sync atcive low */
+#define CR0_FSJUSTIFY       (1 << 4)
+#define CR0_OPM_SLAVE       (0 << 2)
+#define CR0_OPM_MASTER      (3 << 2)
+#define CR0_OPM_I2S_MSST    (3 << 2)  /* Master stereo mode */
+#define CR0_OPM_I2S_MSMO    (2 << 2)  /* Master mono mode */
+#define CR0_OPM_I2S_SLST    (1 << 2)  /* Slave stereo mode */
+#define CR0_OPM_I2S_SLMO    (0 << 2)  /* Slave mono mode */
+#define CR0_SCLKPO          (1 << 1)  /* SCLK Remain HIGH */
+#define CR0_SCLKPH          (1 << 0)  /* Half CLK cycle */
+
+/* Control Register 1 */
+
+#define CR1_PDL(x)          (((x) & 0xff) << 24)		/* padding length */
+#define CR1_SDL(x)          ((((x) - 1) & 0x1f) << 16)	/* data length */
+#define CR1_CLKDIV(x)       ((x) & 0xffff)				/*  clk divider */
+
+/* Control Register 2 */
+#define CR2_FSOS(x)         (((x) & 0x03) << 10)	/* FS/CS Select */
+#define CR2_FS              (1 << 9)	/* FS/CS Signal Level */
+#define CR2_TXEN            (1 << 8)	/* Tx Enable */
+#define CR2_RXEN            (1 << 7)	/* Rx Enable */
+#define CR2_SSPRST          (1 << 6)	/* SSP reset */
+#define CR2_TXFCLR          (1 << 3)	/* TX FIFO Clear */
+#define CR2_RXFCLR          (1 << 2)	/* RX FIFO Clear */
+#define CR2_TXDOE           (1 << 1)	/* TX Data Output Enable */
+#define CR2_SSPEN           (1 << 0)	/* SSP Enable */
+
+/*
+ * Status Register
+ */
+#define SR_RFF				(1 << 0)        /* receive FIFO full */
+#define SR_TFNF				(1 << 1)        /* transmit FIFO not full */
+#define SR_BUSY				(1 << 2)        /* bus is busy */
+#define SR_RFVE(reg)		(((reg) >> 4) & 0x1f)   /* receive  FIFO valid entries */
+#define SR_TFVE(reg)		(((reg) >> 12) & 0x1f)  /* transmit FIFO valid entries */
+
+/*
+ * Feature Register
+ */
+#define FEAR_WIDTH(reg)		((((reg) >>  0) & 0xff) + 1)
+#define FEAR_RXFIFO(reg)	((((reg) >>  8) & 0xff) + 1)
+#define FEAR_TXFIFO(reg)	((((reg) >> 16) & 0xff) + 1)
+#define FEAR_AC97			(1 << 24)
+#define FEAR_I2S			(1 << 25)
+#define FEAR_SPI_MWR		(1 << 26)
+#define FEAR_SSP			(1 << 27)
+#define FEAR_SPDIF			(1 << 28)
+
+#endif
