diff mbox

[U-Boot,v2,05/12] spi: add Faraday FTSPI010 SPI controller support

Message ID 1366277139-29728-6-git-send-email-dantesu@gmail.com
State Superseded
Delegated to: Albert ARIBAUD
Headers show

Commit Message

Kuo-Jung Su April 18, 2013, 9:25 a.m. UTC
From: Kuo-Jung Su <dantesu@faraday-tech.com>

The Faraday FTSSP010 is a multi-function controller
which supports I2S/SPI/SSP/AC97/SPDIF.
This patch simpily implements the SPI mode only.
BTW the DMA and CS/Clock control logic has been
altered since revision 1.19.0. So this patch
would 1st detects the revision id of the underlying
chip, and then switch to the corresponding control
routines.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 drivers/spi/Makefile       |    1 +
 drivers/spi/ftssp010_spi.c |  337 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/ftssp010_spi.h |   86 +++++++++++
 3 files changed, 424 insertions(+)
 create mode 100644 drivers/spi/ftssp010_spi.c
 create mode 100644 drivers/spi/ftssp010_spi.h

Comments

Wolfgang Denk April 18, 2013, 10:56 a.m. UTC | #1
Dear Kuo-Jung Su,

In message <1366277139-29728-6-git-send-email-dantesu@gmail.com> you wrote:
...
> +/* 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)

Ad before: drop these.

> +#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 */

We do not allos I/O accesses throug base addrss plus offset.
Please use proper C structs instead.


Best regards,

Wolfgang Denk
Kuo-Jung Su April 22, 2013, 2:52 a.m. UTC | #2
2013/4/18 Wolfgang Denk <wd@denx.de>:
> Dear Kuo-Jung Su,
>
> In message <1366277139-29728-6-git-send-email-dantesu@gmail.com> you wrote:
> ...
>> +/* 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)
>
> Ad before: drop these.
>

Got it, thanks

>> +#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 */
>
> We do not allos I/O accesses throug base addrss plus offset.
> Please use proper C structs instead.
>
>

Got it, thanks

> Best regards,
>
> Wolfgang Denk
>
> --
> DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
> Sorry, but my karma just ran over your dogma.



--
Best wishes,
Kuo-Jung Su
Jagan Teki Aug. 8, 2013, 1:38 p.m. UTC | #3
Hi Kuo-Jung Su,

Please fix the comments I sent on v4.
http://patchwork.ozlabs.org/patch/242023/

Feel free to ask for any quires.

Thanks,
Jagan.

On Thu, Apr 18, 2013 at 2:55 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>
> The Faraday FTSSP010 is a multi-function controller
> which supports I2S/SPI/SSP/AC97/SPDIF.
> This patch simpily implements the SPI mode only.
> BTW the DMA and CS/Clock control logic has been
> altered since revision 1.19.0. So this patch
> would 1st detects the revision id of the underlying
> chip, and then switch to the corresponding control
> routines.
>
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> ---
>  drivers/spi/Makefile       |    1 +
>  drivers/spi/ftssp010_spi.c |  337 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/spi/ftssp010_spi.h |   86 +++++++++++
>  3 files changed, 424 insertions(+)
>  create mode 100644 drivers/spi/ftssp010_spi.c
>  create mode 100644 drivers/spi/ftssp010_spi.h
>
> 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
> --
> 1.7.9.5
>
> _______________________________________________
> U-Boot mailing list
> U-Boot@lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
Kuo-Jung Su Aug. 9, 2013, 12:47 a.m. UTC | #4
Hi Jagan:

Thanks for the comments, but the [spi bus controller: ftssp010]

has been scheduled after the [Faraday A36x platform support] get approved.

In this way, the entire patch itself looks much better & clean.

Best Wishes

Dante Su

2013/8/8 Jagan Teki <jagannadh.teki@gmail.com>:
> Hi Kuo-Jung Su,
>
> Please fix the comments I sent on v4.
> http://patchwork.ozlabs.org/patch/242023/
>
> Feel free to ask for any quires.
>
> Thanks,
> Jagan.
>
> On Thu, Apr 18, 2013 at 2:55 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>>
>> The Faraday FTSSP010 is a multi-function controller
>> which supports I2S/SPI/SSP/AC97/SPDIF.
>> This patch simpily implements the SPI mode only.
>> BTW the DMA and CS/Clock control logic has been
>> altered since revision 1.19.0. So this patch
>> would 1st detects the revision id of the underlying
>> chip, and then switch to the corresponding control
>> routines.
>>
>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>> ---
>>  drivers/spi/Makefile       |    1 +
>>  drivers/spi/ftssp010_spi.c |  337 ++++++++++++++++++++++++++++++++++++++++++++
>>  drivers/spi/ftssp010_spi.h |   86 +++++++++++
>>  3 files changed, 424 insertions(+)
>>  create mode 100644 drivers/spi/ftssp010_spi.c
>>  create mode 100644 drivers/spi/ftssp010_spi.h
>>
>> 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
>> --
>> 1.7.9.5
>>
>> _______________________________________________
>> U-Boot mailing list
>> U-Boot@lists.denx.de
>> http://lists.denx.de/mailman/listinfo/u-boot
Jagan Teki Aug. 9, 2013, 11:27 a.m. UTC | #5
On Fri, Aug 9, 2013 at 6:17 AM, Kuo-Jung Su <dantesu@gmail.com> wrote:
> Hi Jagan:
>
> Thanks for the comments, but the [spi bus controller: ftssp010]
>
> has been scheduled after the [Faraday A36x platform support] get approved.
>
> In this way, the entire patch itself looks much better & clean.
>
> Best Wishes
>
> Dante Su
>
> 2013/8/8 Jagan Teki <jagannadh.teki@gmail.com>:
>> Hi Kuo-Jung Su,
>>
>> Please fix the comments I sent on v4.
>> http://patchwork.ozlabs.org/patch/242023/
>>
>> Feel free to ask for any quires.
>>
>> Thanks,
>> Jagan.
>>
>> On Thu, Apr 18, 2013 at 2:55 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>>> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>>>
>>> The Faraday FTSSP010 is a multi-function controller
>>> which supports I2S/SPI/SSP/AC97/SPDIF.
>>> This patch simpily implements the SPI mode only.
>>> BTW the DMA and CS/Clock control logic has been
>>> altered since revision 1.19.0. So this patch
>>> would 1st detects the revision id of the underlying
>>> chip, and then switch to the corresponding control
>>> routines.
>>>
>>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>>> ---
>>>  drivers/spi/Makefile       |    1 +
>>>  drivers/spi/ftssp010_spi.c |  337 ++++++++++++++++++++++++++++++++++++++++++++
>>>  drivers/spi/ftssp010_spi.h |   86 +++++++++++
>>>  3 files changed, 424 insertions(+)
>>>  create mode 100644 drivers/spi/ftssp010_spi.c
>>>  create mode 100644 drivers/spi/ftssp010_spi.h
>>>
>>> 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
>>> --
>>> 1.7.9.5
>>>
>>> _______________________________________________
>>> U-Boot mailing list
>>> U-Boot@lists.denx.de
>>> http://lists.denx.de/mailman/listinfo/u-boot

Please see the comments on below patch
http://patchwork.ozlabs.org/patch/265683/

--
Thanks,
Jagan.
Kuo-Jung Su Aug. 12, 2013, 12:37 a.m. UTC | #6
>
> Please see the comments on below patch
> http://patchwork.ozlabs.org/patch/265683/
>
> --

Got it, thanks
Jagan Teki Oct. 3, 2013, 7:53 p.m. UTC | #7
Any update on this.

On Mon, Aug 12, 2013 at 6:07 AM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>>
>> Please see the comments on below patch
>> http://patchwork.ozlabs.org/patch/265683/
>>
>> --
>
> Got it, thanks
>
>
> --
> Best wishes,
> Kuo-Jung Su
diff mbox

Patch

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