On Fri, Dec 20, 2013 at 11:52 PM, Jagannadha Sutradharudu Teki <jagannadha.sutradharudu-t...@xilinx.com> wrote: > From: Kuo-Jung Su <dant...@faraday-tech.com> > > The Faraday FTSSP010 is a multi-function controller > which supports I2S/SPI/SSP/AC97/SPDIF. However This > patch implements only the SPI mode. > > NOTE: > The DMA and CS/Clock control logic has been altered > since hardware revision 1.19.0. So this patch > would first detects the revision id of the underlying > chip, and then switch to the corresponding software > control routines. > > Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com> > Signed-off-by: Jagannadha Sutradharudu Teki <jaga...@xilinx.com> > CC: Tom Rini <tr...@ti.com> > --- > Changes for V8: > - Added spi flash test log > Changes for v7: > - Fixed some checkpatch warnings > - Removed unneeded "inline" in few lenghty defination funcs. > Changes for v6: > - Drop SPI_LSB_FIRST support, because this driver > supports only 8-bit mode access. > - Bug fix: Move chip enable from ftssp010_cs_set() > to spi_cs_activate(), because ftssp010_cs_set() > is supposed to update only the cs signal. > > Changes for v5: > - Use SPDX-License-Identifier > - Add SPI mode checking & setup > - Coding Style cleanup > > Changes for v4: > - Coding Style cleanup. > - Make it a separate patch, rather then a part of > Faraday A36x patch series > - Use macro constants for timeout control > > Changes for v3: > - Coding Style cleanup. > - Drop macros for wirtel()/readl(), call them directly. > - Always insert a blank line between declarations and code. > - Replace all the infinite wait loop with a timeout. > - Add '__iomem' to all the declaration of HW register pointers. > > Changes for v2: > - Coding Style cleanup. > - Use readl(), writel(), clrsetbits_le32() to replace REG() macros. > - Use structure based hardware registers to replace the macro constants. > - Replace BIT() with BIT_MASK(). > > doc/SPI/README.ftssp010_spi_test | 41 ++++ > drivers/spi/Makefile | 1 + > drivers/spi/ftssp010_spi.c | 508 > +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 550 insertions(+) > create mode 100644 doc/SPI/README.ftssp010_spi_test > create mode 100644 drivers/spi/ftssp010_spi.c > > diff --git a/doc/SPI/README.ftssp010_spi_test > b/doc/SPI/README.ftssp010_spi_test > new file mode 100644 > index 0000000..1d86f36 > --- /dev/null > +++ b/doc/SPI/README.ftssp010_spi_test > @@ -0,0 +1,41 @@ > +SPI Flash test on Faraday A369 EVB: > +================================== > + > +U-Boot 2014.01-rc2-g3444b6f (Dec 20 2013 - 10:58:40) > + > +CPU: FA626TE 528 MHz > +AHB: 132 MHz > +APB: 66 MHz > +I2C: ready > +DRAM: 256 MiB > +MMU: on > +NAND: 512 MiB > +MMC: ftsdc010: 0 > +*** Warning - bad CRC, using default environment > + > +In: serial > +Out: serial > +Err: serial > +Net: FTGMAC100#0 > +Hit any key to stop autoboot: 0 > +=> sf probe 0:0 > +SF: Detected MX25L1605D with page size 256 Bytes, erase size 64 KiB, total 2 > MiB > +=> sf read 0x10800000 0 0x400 > +SF: 1024 bytes @ 0x0 Read: OK > +=> md 0x10800000 > +10800000: ea000013 e59ff014 e59ff014 e59ff014 ................ > +10800010: e59ff014 e59ff014 e59ff014 e59ff014 ................ > +10800020: 1ff7b0c0 1ff7b120 1ff7b180 1ff7b1e0 .... ........... > +10800030: 1ff7b240 1ff7b2a0 1ff7b300 deadbeef @............... > +10800040: 10800000 0002c1f0 0007409c 00032048 .........@..H .. > +10800050: 1fd6af40 e10f0000 e3c0001f e38000d3 @............... > +10800060: e129f000 eb000001 eb000223 e12fff1e ..).....#...../. > +10800070: e3a00000 ee070f1e ee080f17 ee070f15 ................ > +10800080: ee070f9a ee110f10 e3c00c03 e3c00087 ................ > +10800090: e3c00a02 e3800002 e3800a01 ee010f10 ................ > +108000a0: e1a0c00e eb007a68 e1a0e00c e1a0f00e ....hz.......... > +108000b0: e1a00000 e1a00000 e1a00000 e1a00000 ................ > +108000c0: e51fd078 e58de000 e14fe000 e58de004 x.........O..... > +108000d0: e3a0d013 e169f00d e1a0e00f e1b0f00e ......i......... > +108000e0: e24dd048 e88d1fff e51f20a0 e892000c H.M...... ...... > +108000f0: e28d0048 e28d5034 e1a0100e e885000f H...4P.......... > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index d5a7143..81b6af6 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -19,6 +19,7 @@ obj-$(CONFIG_CF_SPI) += cf_spi.o > obj-$(CONFIG_CF_QSPI) += cf_qspi.o > obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o > obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o > +obj-$(CONFIG_FTSSP010_SPI) += ftssp010_spi.o > obj-$(CONFIG_ICH_SPI) += ich.o > obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o > obj-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o > diff --git a/drivers/spi/ftssp010_spi.c b/drivers/spi/ftssp010_spi.c > new file mode 100644 > index 0000000..aa3b5a0 > --- /dev/null > +++ b/drivers/spi/ftssp010_spi.c > @@ -0,0 +1,508 @@ > +/* > + * (C) Copyright 2013 > + * Faraday Technology Corporation. <http://www.faraday-tech.com/tw/> > + * Kuo-Jung Su <dant...@gmail.com> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <linux/compat.h> > +#include <asm/io.h> > +#include <malloc.h> > +#include <spi.h> > + > +#ifndef CONFIG_FTSSP010_BASE_LIST > +#define CONFIG_FTSSP010_BASE_LIST { CONFIG_FTSSP010_BASE } > +#endif > + > +#ifndef CONFIG_FTSSP010_GPIO_BASE > +#define CONFIG_FTSSP010_GPIO_BASE 0 > +#endif > + > +#ifndef CONFIG_FTSSP010_GPIO_LIST > +#define CONFIG_FTSSP010_GPIO_LIST { CONFIG_FTSSP010_GPIO_BASE } > +#endif > + > +#ifndef CONFIG_FTSSP010_CLOCK > +#define CONFIG_FTSSP010_CLOCK clk_get_rate("SSP"); > +#endif > + > +#ifndef CONFIG_FTSSP010_TIMEOUT > +#define CONFIG_FTSSP010_TIMEOUT 100 > +#endif > + > +/* FTSSP010 chip 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_LOOP (1 << 7) /* loopback mode */ > +#define CR0_LSB (1 << 6) /* LSB */ > +#define CR0_FSPO (1 << 5) /* fs atcive low (I2S only) */ > +#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) /* clock polarity */ > +#define CR0_SCLKPH (1 << 0) /* clock phase */ > + > +/* Control Register 1 */ > +#define CR1_PDL(x) (((x) & 0xff) << 24) /* padding length */ > +#define CR1_SDL(x) ((((x) - 1) & 0x1f) << 16) /* data length */ > +#define CR1_DIV(x) (((x) - 1) & 0xffff) /* clock divider */ > + > +/* Control Register 2 */ > +#define CR2_CS(x) (((x) & 3) << 10) /* CS/FS select */ > +#define CR2_FS (1 << 9) /* CS/FS signal level */ > +#define CR2_TXEN (1 << 8) /* tx enable */ > +#define CR2_RXEN (1 << 7) /* rx enable */ > +#define CR2_RESET (1 << 6) /* chip reset */ > +#define CR2_TXFC (1 << 3) /* tx fifo Clear */ > +#define CR2_RXFC (1 << 2) /* rx fifo Clear */ > +#define CR2_TXDOE (1 << 1) /* tx data output enable */ > +#define CR2_EN (1 << 0) /* chip enable */ > + > +/* Status Register */ > +#define SR_RFF (1 << 0) /* rx fifo full */ > +#define SR_TFNF (1 << 1) /* tx fifo not full */ > +#define SR_BUSY (1 << 2) /* chip busy */ > +#define SR_RFVE(reg) (((reg) >> 4) & 0x1f) /* rx fifo valid entries */ > +#define SR_TFVE(reg) (((reg) >> 12) & 0x1f) /* tx fifo valid entries */ > + > +/* Feature Register */ > +#define FEAR_BITS(reg) ((((reg) >> 0) & 0xff) + 1) /* data width */ > +#define FEAR_RFSZ(reg) ((((reg) >> 8) & 0xff) + 1) /* rx fifo size */ > +#define FEAR_TFSZ(reg) ((((reg) >> 16) & 0xff) + 1) /* tx fifo size */ > +#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) > + > +/* FTGPIO010 chip registers */ > +struct ftgpio010_regs { > + uint32_t out; /* 0x00: Data Output */ > + uint32_t in; /* 0x04: Data Input */ > + uint32_t dir; /* 0x08: Direction */ > + uint32_t bypass; /* 0x0c: Bypass */ > + uint32_t set; /* 0x10: Data Set */ > + uint32_t clr; /* 0x14: Data Clear */ > + uint32_t pull_up; /* 0x18: Pull-Up Enabled */ > + uint32_t pull_st; /* 0x1c: Pull State (0=pull-down, 1=pull-up) */ > +}; > + > +struct ftssp010_gpio { > + struct ftgpio010_regs *regs; > + uint32_t pin; > +}; > + > +struct ftssp010_spi { > + struct spi_slave slave; > + struct ftssp010_gpio gpio; > + struct ftssp010_regs *regs; > + uint32_t fifo; > + uint32_t mode; > + uint32_t div; > + uint32_t clk; > + uint32_t speed; > + uint32_t revision; > +}; > + > +static inline struct ftssp010_spi *to_ftssp010_spi(struct spi_slave *slave) > +{ > + return container_of(slave, struct ftssp010_spi, slave); > +} > + > +static int get_spi_chip(int bus, struct ftssp010_spi *chip) > +{ > + uint32_t fear, base[] = CONFIG_FTSSP010_BASE_LIST; > + > + if (bus >= ARRAY_SIZE(base) || !base[bus]) > + return -1; > + > + chip->regs = (struct ftssp010_regs *)base[bus]; > + > + chip->revision = readl(&chip->regs->revr); > + > + fear = readl(&chip->regs->fear); > + chip->fifo = min_t(uint32_t, FEAR_TFSZ(fear), FEAR_RFSZ(fear)); > + > + return 0; > +} > + > +static int get_spi_gpio(int bus, struct ftssp010_gpio *chip) > +{ > + uint32_t base[] = CONFIG_FTSSP010_GPIO_LIST; > + > + if (bus >= ARRAY_SIZE(base) || !base[bus]) > + return -1; > + > + chip->regs = (struct ftgpio010_regs *)(base[bus] & 0xfff00000); > + chip->pin = base[bus] & 0x1f; > + > + /* make it an output pin */ > + setbits_le32(&chip->regs->dir, 1 << chip->pin); > + > + return 0; > +} > + > +static int ftssp010_wait(struct ftssp010_spi *chip) > +{ > + struct ftssp010_regs *regs = chip->regs; > + int ret = -1; > + ulong t; > + > + /* wait until device idle */ > + for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) { > + if (readl(®s->sr) & SR_BUSY) > + continue; > + ret = 0; > + break; > + } > + > + if (ret) > + puts("ftspi010: busy timeout\n"); > + > + return ret; > +} > + > +static int ftssp010_wait_tx(struct ftssp010_spi *chip) > +{ > + struct ftssp010_regs *regs = chip->regs; > + int ret = -1; > + ulong t; > + > + /* wait until tx fifo not full */ > + for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) { > + if (!(readl(®s->sr) & SR_TFNF)) > + continue; > + ret = 0; > + break; > + } > + > + if (ret) > + puts("ftssp010: tx timeout\n"); > + > + return ret; > +} > + > +static int ftssp010_wait_rx(struct ftssp010_spi *chip) > +{ > + struct ftssp010_regs *regs = chip->regs; > + int ret = -1; > + ulong t; > + > + /* wait until rx fifo not empty */ > + for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) { > + if (!SR_RFVE(readl(®s->sr))) > + continue; > + ret = 0; > + break; > + } > + > + if (ret) > + puts("ftssp010: rx timeout\n"); > + > + return ret; > +} > + > +static int ftssp010_spi_work_transfer_v2(struct ftssp010_spi *chip, > + const void *tx_buf, void *rx_buf, int len, uint flags) > +{ > + struct ftssp010_regs *regs = chip->regs; > + 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) { > + ftssp010_wait_tx(chip); > + writel(*txb++, ®s->dr); > + } > + xmsk |= CR2_TXEN | CR2_TXDOE; > + if ((readl(®s->cr[2]) & xmsk) != xmsk) > + setbits_le32(®s->cr[2], xmsk); > + } > + if (rx_buf) { > + xmsk |= CR2_RXEN; > + if ((readl(®s->cr[2]) & xmsk) != xmsk) > + setbits_le32(®s->cr[2], xmsk); > + for (i = 0; i < depth; ++i) { > + ftssp010_wait_rx(chip); > + *rxb++ = (uint8_t)readl(®s->dr); > + } > + } > + > + len -= depth; > + } > + > + return 0; > +} > + > +static int ftssp010_spi_work_transfer_v1(struct ftssp010_spi *chip, > + const void *tx_buf, void *rx_buf, int len, uint flags) > +{ > + struct ftssp010_regs *regs = chip->regs; > + 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) { > + ftssp010_wait_tx(chip); > + writel(txb ? (*txb++) : 0, ®s->dr); > + } > + for (i = 0; i < depth; ++i) { > + ftssp010_wait_rx(chip); > + tmp = readl(®s->dr); > + if (rxb) > + *rxb++ = (uint8_t)tmp; > + } > + > + len -= depth; > + } > + > + return 0; > +} > + > +static void ftssp010_cs_set(struct ftssp010_spi *chip, int high) > +{ > + struct ftssp010_regs *regs = chip->regs; > + struct ftssp010_gpio *gpio = &chip->gpio; > + uint32_t mask; > + > + /* cs pull high/low */ > + if (chip->revision >= 0x11900) { > + mask = CR2_CS(chip->slave.cs) | (high ? CR2_FS : 0); > + writel(mask, ®s->cr[2]); > + } else if (gpio->regs) { > + mask = 1 << gpio->pin; > + if (high) > + writel(mask, &gpio->regs->set); > + else > + writel(mask, &gpio->regs->clr); > + } > + > + /* extra delay for signal propagation */ > + udelay_masked(1); > +} > + > +/* > + * 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_spi chip; > + > + if (get_spi_chip(bus, &chip)) > + return 0; > + > + if (!cs) > + return 1; > + else if ((cs < 4) && (chip.revision >= 0x11900)) > + return 1; > + > + return 0; > +} > + > +/* > + * 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_spi *chip = to_ftssp010_spi(slave); > + struct ftssp010_regs *regs = chip->regs; > + > + /* cs pull */ > + if (chip->mode & SPI_CS_HIGH) > + ftssp010_cs_set(chip, 1); > + else > + ftssp010_cs_set(chip, 0); > + > + /* chip enable + fifo clear */ > + setbits_le32(®s->cr[2], CR2_EN | CR2_TXFC | CR2_RXFC); > +} > + > +/* > + * 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_spi *chip = to_ftssp010_spi(slave); > + > + /* wait until chip idle */ > + ftssp010_wait(chip); > + > + /* cs pull */ > + if (chip->mode & SPI_CS_HIGH) > + ftssp010_cs_set(chip, 0); > + else > + ftssp010_cs_set(chip, 1); > +} > + > +void spi_init(void) > +{ > + /* nothing to do */ > +} > + > +struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode) > +{ > + struct ftssp010_spi *chip; > + > + if (mode & SPI_3WIRE) { > + puts("ftssp010: can't do 3-wire\n"); > + return NULL; > + } > + > + if (mode & SPI_SLAVE) { > + puts("ftssp010: can't do slave mode\n"); > + return NULL; > + } > + > + if (mode & SPI_PREAMBLE) { > + puts("ftssp010: can't skip preamble bytes\n"); > + return NULL; > + } > + > + if (!spi_cs_is_valid(bus, cs)) { > + puts("ftssp010: invalid (bus, cs)\n"); > + return NULL; > + } > + > + chip = spi_alloc_slave(struct ftssp010_spi, bus, cs); > + if (!chip) > + return NULL; > + > + if (get_spi_chip(bus, chip)) > + goto free_out; > + > + if (chip->revision < 0x11900 && get_spi_gpio(bus, &chip->gpio)) { > + puts("ftssp010: Before revision 1.19.0, its clock & cs are\n" > + "controlled by tx engine which is not synced with rx > engine,\n" > + "so the clock & cs might be shutdown before rx engine\n" > + "finishs its jobs.\n" > + "If possible, please add a dedicated gpio for it.\n"); > + } > + > + chip->mode = mode; > + chip->clk = CONFIG_FTSSP010_CLOCK; > + chip->div = 2; > + if (max_hz) { > + while (chip->div < 0xffff) { > + if ((chip->clk / (2 * chip->div)) <= max_hz) > + break; > + chip->div += 1; > + } > + } > + chip->speed = chip->clk / (2 * chip->div); > + > + return &chip->slave; > + > +free_out: > + free(chip); > + return NULL; > +} > + > +void spi_free_slave(struct spi_slave *slave) > +{ > + free(slave); > +} > + > +int spi_claim_bus(struct spi_slave *slave) > +{ > + struct ftssp010_spi *chip = to_ftssp010_spi(slave); > + struct ftssp010_regs *regs = chip->regs; > + > + writel(CR1_SDL(8) | CR1_DIV(chip->div), ®s->cr[1]); > + > + if (chip->revision >= 0x11900) { > + writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO | CR0_FLASH, > + ®s->cr[0]); > + writel(CR2_TXFC | CR2_RXFC, > + ®s->cr[2]); > + } else { > + writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO, > + ®s->cr[0]); > + writel(CR2_TXFC | CR2_RXFC | CR2_EN | CR2_TXDOE, > + ®s->cr[2]); > + } > + > + if (chip->mode & SPI_LOOP) > + setbits_le32(®s->cr[0], CR0_LOOP); > + > + if (chip->mode & SPI_CPOL) > + setbits_le32(®s->cr[0], CR0_SCLKPO); > + > + if (chip->mode & SPI_CPHA) > + setbits_le32(®s->cr[0], CR0_SCLKPH); > + > + spi_cs_deactivate(slave); > + > + return 0; > +} > + > +void spi_release_bus(struct spi_slave *slave) > +{ > + struct ftssp010_spi *chip = to_ftssp010_spi(slave); > + struct ftssp010_regs *regs = chip->regs; > + > + writel(0, ®s->cr[2]); > +} > + > +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, > + const void *dout, void *din, unsigned long flags) > +{ > + struct ftssp010_spi *chip = to_ftssp010_spi(slave); > + uint32_t len = bitlen >> 3; > + > + if (flags & SPI_XFER_BEGIN) > + spi_cs_activate(slave); > + > + if (chip->revision >= 0x11900) > + ftssp010_spi_work_transfer_v2(chip, dout, din, len, flags); > + else > + ftssp010_spi_work_transfer_v1(chip, dout, din, len, flags); > + > + if (flags & SPI_XFER_END) > + spi_cs_deactivate(slave); > + > + return 0; > +}
Applied to u-boot-spi/master -- Thanks, Jagan. _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot