diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index d08609e..947d60e 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..4247c8c
--- /dev/null
+++ b/drivers/spi/ftssp010_spi.c
@@ -0,0 +1,337 @@
+/*
+ * 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 <asm/io.h>
+#include <spi.h>
+#include <malloc.h>
+
+#include "ftssp010_spi.h"
+
+struct ftssp010_chip {
+	void    *iobase;
+	uint32_t fifo;
+	uint32_t rev;
+	uint32_t div;
+	uint32_t mode;
+
+	struct {
+		uint32_t iobase;
+		uint32_t pin;
+	} gpio;
+};
+
+static struct ftssp010_chip chip_list[] = {
+#if defined(CONFIG_FTSSP010_BASE) || defined(CONFIG_FTSSP010_BASE0)
+	{
+		.iobase = (void *)CONFIG_FTSSP010_BASE,
+#ifdef CONFIG_FTSSP010_GPIO_BASE
+		.gpio = { CONFIG_FTSSP010_GPIO_BASE, CONFIG_FTSSP010_GPIO_PIN },
+#endif
+	},
+#endif
+#ifdef CONFIG_FTSSP010_BASE1
+	{ .iobase = (void *)CONFIG_FTSSP010_BASE1, },
+#endif
+#ifdef CONFIG_FTSSP010_BASE2
+	{ .iobase = (void *)CONFIG_FTSSP010_BASE2, },
+#endif
+#ifdef CONFIG_FTSSP010_BASE3
+	{ .iobase = (void *)CONFIG_FTSSP010_BASE3, },
+#endif
+};
+
+/* Register access macros */
+#define SPI_READ(r)			le32_to_cpu(readl(r))
+#define SPI_WRITE(v, r)		writel(cpu_to_le32(v), r)
+#define SPI_SETBITS(m, r)	setbits_le32(r, m)
+#define SPI_CLRBITS(m, r)	clrbits_le32(r, m)
+
+#ifdef CONFIG_FTSSP010_GPIO_BASE
+#define SPI_GPIO_READ(p, r)	\
+	le32_to_cpu(readl((p)->gpio.iobase + (r)))
+#define SPI_GPIO_WRITE(p, v, r)	\
+	writel(cpu_to_le32(v), (p)->gpio.iobase + (r))
+#define SPI_GPIO_SETBITS(p, m, r)	\
+	setbits_le32((p)->gpio.iobase + (r), m)
+#define SPI_GPIO_CLRBITS(p, m, r)	\
+	clrbits_le32((p)->gpio.iobase + (r), m)
+#endif /* #ifdef CONFIG_FTSSP010_GPIO_BASE */
+
+static int ftssp010_spi_work_transfer_v1_19(struct ftssp010_chip *chip,
+	const void *tx_buf, void *rx_buf, int len, uint flags)
+{
+	struct ftssp010_regs *regs = chip->iobase;
+	const uint8_t *txb = tx_buf;
+	uint8_t       *rxb = rx_buf;
+
+	while (len > 0) {
+		int i, depth = min(chip->fifo >> 2, len);
+		uint32_t xmsk = 0;
+
+		if (tx_buf) {
+			for (i = 0; i < depth; ++i) {
+				while (!(SPI_READ(&regs->sr) & SR_TFNF))
+					;
+				SPI_WRITE(*txb++, &regs->dr);
+			}
+			xmsk |= CR2_TXEN | CR2_TXDOE;
+			if ((SPI_READ(&regs->cr[2]) & xmsk) != xmsk)
+				SPI_SETBITS(xmsk, &regs->cr[2]);
+		}
+		if (rx_buf) {
+			xmsk |= CR2_RXEN;
+			if ((SPI_READ(&regs->cr[2]) & xmsk) != xmsk)
+				SPI_SETBITS(xmsk, &regs->cr[2]);
+			for (i = 0; i < depth; ++i) {
+				while (!SR_RFVE(SPI_READ(&regs->sr)))
+					;
+				*rxb++ = (uint8_t)SPI_READ(&regs->dr);
+			}
+		}
+
+		len -= depth;
+	}
+
+	return 0;
+}
+
+static int ftssp010_spi_work_transfer(struct ftssp010_chip *chip,
+	const void *tx_buf, void *rx_buf, int len, uint flags)
+{
+	struct ftssp010_regs *regs = chip->iobase;
+	const uint8_t *txb = tx_buf;
+	uint8_t       *rxb = rx_buf;
+
+	while (len > 0) {
+		int i, depth = min(chip->fifo >> 2, len);
+		uint32_t tmp;
+
+		for (i = 0; i < depth; ++i) {
+			while (!(SPI_READ(&regs->sr) & SR_TFNF))
+				;
+			SPI_WRITE(txb ? (*txb++) : 0, &regs->dr);
+		}
+		for (i = 0; i < depth; ++i) {
+			while (!SR_RFVE(SPI_READ(&regs->sr)))
+				;
+			tmp = SPI_READ(&regs->dr);
+			if (rxb)
+				*rxb++ = (uint8_t)tmp;
+		}
+
+		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)
+{
+	struct ftssp010_chip *chip;
+	struct ftssp010_regs *regs;
+	uint32_t txfifo, rxfifo;
+
+	if (bus >= ARRAY_SIZE(chip_list))
+		return 0;
+
+	chip = chip_list + bus;
+	regs = chip->iobase;
+	chip->rev = SPI_READ(&regs->revr);
+	txfifo = FEAR_TXFIFO(SPI_READ(&regs->fear));
+	rxfifo = FEAR_RXFIFO(SPI_READ(&regs->fear));
+	chip->fifo = min(txfifo, rxfifo);
+
+	printf("ftssp010: rev.=0x%08X, fifo=%d\n", chip->rev, chip->fifo);
+
+	if (chip->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 */
+		SPI_GPIO_SETBITS(chip, 1 << chip->gpio.pin, 0x08);
+#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)
+{
+	struct ftssp010_chip *chip = chip_list + slave->bus;
+	struct ftssp010_regs *regs = chip->iobase;
+
+	/* cs pull low */
+	if (chip->rev >= 0x00011900) {
+		SPI_WRITE((slave->cs << 10) | CR2_SSPEN | CR2_TXFCLR
+			| CR2_RXFCLR, &regs->cr[2]);
+	} else {
+#ifdef CONFIG_FTSSP010_GPIO_BASE
+		SPI_GPIO_SETBITS(chip, 1 << chip->gpio.pin, 0x14);
+#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)
+{
+	struct ftssp010_chip *chip = chip_list + slave->bus;
+	struct ftssp010_regs *regs = chip->iobase;
+
+	/* wait until device idle */
+	while (SPI_READ(&regs->sr) & SR_BUSY)
+		;
+
+	/* cs pull high */
+	if (chip->rev >= 0x00011900) {
+		SPI_WRITE((slave->cs << 10) | CR2_FS, &regs->cr[2]);
+	} else {
+#ifdef CONFIG_FTSSP010_GPIO_BASE
+		SPI_GPIO_SETBITS(chip, 1 << chip->gpio.pin, 0x10);
+#endif
+	}
+	udelay_masked(1);
+}
+
+void spi_init(void)
+{
+}
+
+struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode)
+{
+	uint32_t clk, div;
+	struct spi_slave *ss;
+	struct ftssp010_chip *chip;
+
+	if (!spi_cs_is_valid(bus, cs))
+		return NULL;
+
+	if (mode != SPI_MODE_0) {
+		printf("ftssp010: MODE%d is not supported\n", mode);
+		return NULL;
+	}
+
+	ss = malloc(sizeof(struct spi_slave));
+	if (!ss)
+		return NULL;
+
+	ss->bus = bus;
+	ss->cs  = cs;
+
+#ifdef CONFIG_FTSSP010_SCLK
+	clk = CONFIG_FTSSP010_SCLK;
+#else
+	clk = clk_get_rate("SSP");
+#endif
+	if (max_hz > 0) {
+		for (div = 0; div < 0xFFFF; ++div) {
+			if ((clk / (2 * (div + 1))) <= max_hz)
+				break;
+		}
+	} else {
+		div = 7;
+	}
+
+	chip = chip_list + bus;
+	chip->div  = div;
+	chip->mode = mode;
+
+	printf("ftssp010: bus=%d, hz=%u\n", bus, (clk / (2 * (div + 1))));
+
+	return ss;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+	free(slave);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+	struct ftssp010_chip *chip = chip_list + slave->bus;
+	struct ftssp010_regs *regs = chip->iobase;
+
+	SPI_WRITE(CR1_SDL(8) | CR1_CLKDIV(chip->div), &regs->cr[1]);
+
+	if (chip->rev >= 0x00011900) {
+		SPI_WRITE(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO | CR0_FLASH,
+			&regs->cr[0]);
+		SPI_WRITE(CR2_TXFCLR | CR2_RXFCLR,
+			&regs->cr[2]);
+	} else {
+		SPI_WRITE(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO,
+			&regs->cr[0]);
+		SPI_WRITE(CR2_TXFCLR | CR2_RXFCLR | CR2_SSPEN | CR2_TXDOE,
+			&regs->cr[2]);
+	}
+
+	spi_cs_deactivate(slave);
+
+	return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+	struct ftssp010_chip *chip = chip_list + slave->bus;
+	struct ftssp010_regs *regs = chip->iobase;
+
+	SPI_WRITE(0, &regs->cr[2]);
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+			 const void *dout, void *din, unsigned long flags)
+{
+	struct ftssp010_chip *chip = chip_list + slave->bus;
+	uint32_t len = bitlen >> 3;
+
+	if (flags & SPI_XFER_BEGIN)
+		spi_cs_activate(slave);
+
+	if (chip->rev >= 0x00011900)
+		ftssp010_spi_work_transfer_v1_19(chip, dout, din, len, flags);
+	else
+		ftssp010_spi_work_transfer(chip, 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..5ad7c47
--- /dev/null
+++ b/drivers/spi/ftssp010_spi.h
@@ -0,0 +1,86 @@
+/*
+ * 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 */
+struct ftssp010_regs {
+	uint32_t cr[3];/* control register */
+	uint32_t sr;   /* status register */
+	uint32_t icr;  /* interrupt control register */
+	uint32_t isr;  /* interrupt status register */
+	uint32_t dr;   /* data register */
+	uint32_t rsvd[17];
+	uint32_t revr; /* revision register */
+	uint32_t fear; /* 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
