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

Jagan Teki jagannadh.teki at gmail.com
Fri Aug 9 13:27:42 CEST 2013


On Fri, Aug 9, 2013 at 6:17 AM, Kuo-Jung Su <dantesu at 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 at 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 at gmail.com> wrote:
>>> From: Kuo-Jung Su <dantesu at 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 at 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 at 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 at 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 at 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.


More information about the U-Boot mailing list