[U-Boot] [PATCH v2 05/12] spi: add Faraday FTSPI010 SPI controller support
Kuo-Jung Su
dantesu at gmail.com
Fri Aug 9 02:47:51 CEST 2013
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(®s->sr) & SR_TFNF))
>> + ;
>> + SPI_WRITE(*txb++, ®s->dr);
>> + }
>> + xmsk |= CR2_TXEN | CR2_TXDOE;
>> + if ((SPI_READ(®s->cr[2]) & xmsk) != xmsk)
>> + SPI_SETBITS(xmsk, ®s->cr[2]);
>> + }
>> + if (rx_buf) {
>> + xmsk |= CR2_RXEN;
>> + if ((SPI_READ(®s->cr[2]) & xmsk) != xmsk)
>> + SPI_SETBITS(xmsk, ®s->cr[2]);
>> + for (i = 0; i < depth; ++i) {
>> + while (!SR_RFVE(SPI_READ(®s->sr)))
>> + ;
>> + *rxb++ = (uint8_t)SPI_READ(®s->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(®s->sr) & SR_TFNF))
>> + ;
>> + SPI_WRITE(txb ? (*txb++) : 0, ®s->dr);
>> + }
>> + for (i = 0; i < depth; ++i) {
>> + while (!SR_RFVE(SPI_READ(®s->sr)))
>> + ;
>> + tmp = SPI_READ(®s->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(®s->revr);
>> + txfifo = FEAR_TXFIFO(SPI_READ(®s->fear));
>> + rxfifo = FEAR_RXFIFO(SPI_READ(®s->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, ®s->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(®s->sr) & SR_BUSY)
>> + ;
>> +
>> + /* cs pull high */
>> + if (chip->rev >= 0x00011900) {
>> + SPI_WRITE((slave->cs << 10) | CR2_FS, ®s->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), ®s->cr[1]);
>> +
>> + if (chip->rev >= 0x00011900) {
>> + SPI_WRITE(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO | CR0_FLASH,
>> + ®s->cr[0]);
>> + SPI_WRITE(CR2_TXFCLR | CR2_RXFCLR,
>> + ®s->cr[2]);
>> + } else {
>> + SPI_WRITE(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO,
>> + ®s->cr[0]);
>> + SPI_WRITE(CR2_TXFCLR | CR2_RXFCLR | CR2_SSPEN | CR2_TXDOE,
>> + ®s->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, ®s->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
--
Best wishes,
Kuo-Jung Su
More information about the U-Boot
mailing list