diff options
Diffstat (limited to 'drivers/spi')
| -rw-r--r-- | drivers/spi/Makefile | 1 | ||||
| -rw-r--r-- | drivers/spi/cf_qspi.c | 373 | ||||
| -rw-r--r-- | drivers/spi/kirkwood_spi.c | 5 | ||||
| -rw-r--r-- | drivers/spi/mpc8xxx_spi.c | 4 | ||||
| -rw-r--r-- | drivers/spi/mxs_spi.c | 58 | ||||
| -rw-r--r-- | drivers/spi/tegra_spi.c | 6 | ||||
| -rw-r--r-- | drivers/spi/xilinx_spi.c | 1 |
7 files changed, 417 insertions, 31 deletions
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 80b981f10..f0b82c67f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -32,6 +32,7 @@ COBJS-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o COBJS-$(CONFIG_BFIN_SPI) += bfin_spi.o COBJS-$(CONFIG_CF_SPI) += cf_spi.o +COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o diff --git a/drivers/spi/cf_qspi.c b/drivers/spi/cf_qspi.c new file mode 100644 index 000000000..72dd1a520 --- /dev/null +++ b/drivers/spi/cf_qspi.c @@ -0,0 +1,373 @@ +/* + * Freescale Coldfire Queued SPI driver + * + * NOTE: + * This driver is written to transfer 8 bit at-a-time and uses the dedicated + * SPI slave select pins as bit-banged GPIO to work with spi_flash subsystem. + * + * + * Copyright (C) 2011 Ruggedcom, Inc. + * Richard Retanubun (richardretanubun@freescale.com) + * + * See file CREDITS for list of people who contributed to this project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <spi.h> +#include <asm/immap.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define clamp(x, low, high) (min(max(low, x), high)) +#define to_cf_qspi_slave(s) container_of(s, struct cf_qspi_slave, s) + +struct cf_qspi_slave { + struct spi_slave slave; /* Specific bus:cs ID for each device */ + qspi_t *regs; /* Pointer to SPI controller registers */ + u16 qmr; /* QMR: Queued Mode Register */ + u16 qwr; /* QWR: Queued Wrap Register */ + u16 qcr; /* QCR: Queued Command Ram */ +}; + +/* Register write wrapper functions */ +static void write_qmr(volatile qspi_t *qspi, u16 val) { qspi->mr = val; } +static void write_qdlyr(volatile qspi_t *qspi, u16 val) { qspi->dlyr = val; } +static void write_qwr(volatile qspi_t *qspi, u16 val) { qspi->wr = val; } +static void write_qir(volatile qspi_t *qspi, u16 val) { qspi->ir = val; } +static void write_qar(volatile qspi_t *qspi, u16 val) { qspi->ar = val; } +static void write_qdr(volatile qspi_t *qspi, u16 val) { qspi->dr = val; } +/* Register read wrapper functions */ +static u16 read_qdlyr(volatile qspi_t *qspi) { return qspi->dlyr; } +static u16 read_qwr(volatile qspi_t *qspi) { return qspi->wr; } +static u16 read_qir(volatile qspi_t *qspi) { return qspi->ir; } +static u16 read_qdr(volatile qspi_t *qspi) { return qspi->dr; } + +/* These call points may be different for each ColdFire CPU */ +extern void cfspi_port_conf(void); +static void cfspi_cs_activate(uint bus, uint cs, uint cs_active_high); +static void cfspi_cs_deactivate(uint bus, uint cs, uint cs_active_high); + +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} +void spi_release_bus(struct spi_slave *slave) +{ +} + +__attribute__((weak)) +void spi_init(void) +{ + cfspi_port_conf(); +} + +__attribute__((weak)) +void spi_cs_activate(struct spi_slave *slave) +{ + struct cf_qspi_slave *dev = to_cf_qspi_slave(slave); + + cfspi_cs_activate(slave->bus, slave->cs, !(dev->qwr & QSPI_QWR_CSIV)); +} + +__attribute__((weak)) +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct cf_qspi_slave *dev = to_cf_qspi_slave(slave); + + cfspi_cs_deactivate(slave->bus, slave->cs, !(dev->qwr & QSPI_QWR_CSIV)); +} + +__attribute__((weak)) +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + /* Only 1 bus and 4 chipselect per controller */ + if (bus == 0 && (cs >= 0 && cs < 4)) + return 1; + else + return 0; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct cf_qspi_slave *dev = to_cf_qspi_slave(slave); + + free(dev); +} + +/* Translate information given by spi_setup_slave to members of cf_qspi_slave */ +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct cf_qspi_slave *dev = NULL; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + dev = malloc(sizeof(struct cf_qspi_slave)); + if (!dev) + return NULL; + + /* Initialize to known value */ + dev->slave.bus = bus; + dev->slave.cs = cs; + dev->regs = (qspi_t *)MMAP_QSPI; + dev->qmr = 0; + dev->qwr = 0; + dev->qcr = 0; + + + /* Map max_hz to QMR[BAUD] */ + if (max_hz == 0) /* Go as fast as possible */ + dev->qmr = 2u; + else /* Get the closest baud rate */ + dev->qmr = clamp(((gd->bus_clk >> 2) + max_hz - 1)/max_hz, + 2u, 255u); + + /* Map mode to QMR[CPOL] and QMR[CPHA] */ + if (mode & SPI_CPOL) + dev->qmr |= QSPI_QMR_CPOL; + + if (mode & SPI_CPHA) + dev->qmr |= QSPI_QMR_CPHA; + + /* Hardcode bit length to 8 bit per transter */ + dev->qmr |= QSPI_QMR_BITS_8; + + /* Set QMR[MSTR] to enable QSPI as master */ + dev->qmr |= QSPI_QMR_MSTR; + + /* + * Set QCR and QWR to default values for spi flash operation. + * If more custom QCR and QRW are needed, overload mode variable + */ + dev->qcr = (QSPI_QDR_CONT | QSPI_QDR_BITSE); + + if (!(mode & SPI_CS_HIGH)) + dev->qwr |= QSPI_QWR_CSIV; + + return &dev->slave; +} + +/* Transfer 8 bit at a time */ +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct cf_qspi_slave *dev = to_cf_qspi_slave(slave); + volatile qspi_t *qspi = dev->regs; + u8 *txbuf = (u8 *)dout; + u8 *rxbuf = (u8 *)din; + u32 count = ((bitlen / 8) + (bitlen % 8 ? 1 : 0)); + u32 n, i = 0; + + /* Sanitize arguments */ + if (slave == NULL) { + printf("%s: NULL slave ptr\n", __func__); + return -1; + } + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* There is something to send, lets process it. spi_xfer is also called + * just to toggle chip select, so bitlen of 0 is valid */ + if (count > 0) { + /* + * NOTE: Since chip select is driven as a bit-bang-ed GPIO + * using spi_cs_activate() and spi_cs_deactivate(), + * the chip select settings inside the controller + * (i.e. QCR[CONT] and QWR[CSIV]) are moot. The bits are set to + * keep the controller settings consistent with the actual + * operation of the bus. + */ + + /* Write the slave device's settings for the controller.*/ + write_qmr(qspi, dev->qmr); + write_qwr(qspi, dev->qwr); + + /* Limit transfer to 16 at a time */ + n = min(count, 16u); + do { + /* Setup queue end point */ + write_qwr(qspi, ((read_qwr(qspi) & QSPI_QWR_ENDQP_MASK) + | QSPI_QWR_ENDQP((n-1)))); + + /* Write Command RAM */ + write_qar(qspi, QSPI_QAR_CMD); + for (i = 0; i < n; ++i) + write_qdr(qspi, dev->qcr); + + /* Write TxBuf, if none given, fill with ZEROes */ + write_qar(qspi, QSPI_QAR_TRANS); + if (txbuf) { + for (i = 0; i < n; ++i) + write_qdr(qspi, *txbuf++); + } else { + for (i = 0; i < n; ++i) + write_qdr(qspi, 0); + } + + /* Clear QIR[SPIF] by writing a 1 to it */ + write_qir(qspi, read_qir(qspi) | QSPI_QIR_SPIF); + /* Set QDLYR[SPE] to start sending */ + write_qdlyr(qspi, read_qdlyr(qspi) | QSPI_QDLYR_SPE); + + /* Poll QIR[SPIF] for transfer completion */ + while ((read_qir(qspi) & QSPI_QIR_SPIF) != 1) + udelay(1); + + /* If given read RxBuf, load data to it */ + if (rxbuf) { + write_qar(qspi, QSPI_QAR_RECV); + for (i = 0; i < n; ++i) + *rxbuf++ = read_qdr(qspi); + } + + /* Decrement count */ + count -= n; + } while (count); + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +} + +/* Each MCF CPU may have different pin assignments for chip selects. */ +#if defined(CONFIG_M5271) +/* Assert chip select, val = [1|0] , dir = out, mode = GPIO */ +void cfspi_cs_activate(uint bus, uint cs, uint cs_active_high) +{ + debug("%s: bus %d cs %d cs_active_high %d\n", + __func__, bus, cs, cs_active_high); + + switch (cs) { + case 0: /* QSPI_CS[0] = PQSPI[3] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x08); + else + mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xF7); + + mbar_writeByte(MCF_GPIO_PDDR_QSPI, + mbar_readByte(MCF_GPIO_PDDR_QSPI) | 0x08); + + mbar_writeByte(MCF_GPIO_PAR_QSPI, + mbar_readByte(MCF_GPIO_PAR_QSPI) & 0xDF); + break; + case 1: /* QSPI_CS[1] = PQSPI[4] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x10); + else + mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xEF); + + mbar_writeByte(MCF_GPIO_PDDR_QSPI, + mbar_readByte(MCF_GPIO_PDDR_QSPI) | 0x10); + + mbar_writeByte(MCF_GPIO_PAR_QSPI, + mbar_readByte(MCF_GPIO_PAR_QSPI) & 0x3F); + break; + case 2: /* QSPI_CS[2] = PTIMER[7] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x80); + else + mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0x7F); + + mbar_writeByte(MCF_GPIO_PDDR_TIMER, + mbar_readByte(MCF_GPIO_PDDR_TIMER) | 0x80); + + mbar_writeShort(MCF_GPIO_PAR_TIMER, + mbar_readShort(MCF_GPIO_PAR_TIMER) & 0x3FFF); + break; + case 3: /* QSPI_CS[3] = PTIMER[3] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x08); + else + mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0xF7); + + mbar_writeByte(MCF_GPIO_PDDR_TIMER, + mbar_readByte(MCF_GPIO_PDDR_TIMER) | 0x08); + + mbar_writeShort(MCF_GPIO_PAR_TIMER, + mbar_readShort(MCF_GPIO_PAR_TIMER) & 0xFF3F); + break; + } +} + +/* Deassert chip select, val = [1|0], dir = in, mode = GPIO + * direction set as IN to undrive the pin, external pullup/pulldown will bring + * bus to deassert state. + */ +void cfspi_cs_deactivate(uint bus, uint cs, uint cs_active_high) +{ + debug("%s: bus %d cs %d cs_active_high %d\n", + __func__, bus, cs, cs_active_high); + + switch (cs) { + case 0: /* QSPI_CS[0] = PQSPI[3] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xF7); + else + mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x08); + + mbar_writeByte(MCF_GPIO_PDDR_QSPI, + mbar_readByte(MCF_GPIO_PDDR_QSPI) & 0xF7); + + mbar_writeByte(MCF_GPIO_PAR_QSPI, + mbar_readByte(MCF_GPIO_PAR_QSPI) & 0xDF); + break; + case 1: /* QSPI_CS[1] = PQSPI[4] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xEF); + else + mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x10); + + mbar_writeByte(MCF_GPIO_PDDR_QSPI, + mbar_readByte(MCF_GPIO_PDDR_QSPI) & 0xEF); + + mbar_writeByte(MCF_GPIO_PAR_QSPI, + mbar_readByte(MCF_GPIO_PAR_QSPI) & 0x3F); + break; + case 2: /* QSPI_CS[2] = PTIMER[7] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0x7F); + else + mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x80); + + mbar_writeByte(MCF_GPIO_PDDR_TIMER, + mbar_readByte(MCF_GPIO_PDDR_TIMER) & 0x7F); + + mbar_writeShort(MCF_GPIO_PAR_TIMER, + mbar_readShort(MCF_GPIO_PAR_TIMER) & 0x3FFF); + break; + case 3: /* QSPI_CS[3] = PTIMER[3] */ + if (cs_active_high) + mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0xF7); + else + mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x08); + + mbar_writeByte(MCF_GPIO_PDDR_TIMER, + mbar_readByte(MCF_GPIO_PDDR_TIMER) & 0xF7); + + mbar_writeShort(MCF_GPIO_PAR_TIMER, + mbar_readShort(MCF_GPIO_PAR_TIMER) & 0xFF3F); + break; + } +} +#endif /* CONFIG_M5271 */ diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c index f4523a392..a7cda751b 100644 --- a/drivers/spi/kirkwood_spi.c +++ b/drivers/spi/kirkwood_spi.c @@ -56,8 +56,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, writel(~KWSPI_CSN_ACT | KWSPI_SMEMRDY, &spireg->ctrl); /* calculate spi clock prescaller using max_hz */ - data = ((CONFIG_SYS_TCLK / 2) / max_hz) & KWSPI_CLKPRESCL_MASK; - data |= 0x10; + data = ((CONFIG_SYS_TCLK / 2) / max_hz) + 0x10; + data = data < KWSPI_CLKPRESCL_MIN ? KWSPI_CLKPRESCL_MIN : data; + data = data > KWSPI_CLKPRESCL_MASK ? KWSPI_CLKPRESCL_MASK : data; /* program spi clock prescaller using max_hz */ writel(KWSPI_ADRLEN_3BYTE | data, &spireg->cfg); diff --git a/drivers/spi/mpc8xxx_spi.c b/drivers/spi/mpc8xxx_spi.c index 44ab39dd3..4e46041df 100644 --- a/drivers/spi/mpc8xxx_spi.c +++ b/drivers/spi/mpc8xxx_spi.c @@ -124,6 +124,8 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, * len > 16 0 */ + spi->mode &= ~SPI_MODE_EN; + if (bitlen <= 16) { if (bitlen <= 4) spi->mode = (spi->mode & 0xff0fffff) | @@ -138,6 +140,8 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, dout += 4; } + spi->mode |= SPI_MODE_EN; + spi->tx = tmpdout; /* Write the data out */ debug("*** spi_xfer: ... %08x written\n", tmpdout); diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c index 168dbe497..42e4c9952 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -224,8 +224,10 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave, struct mxs_dma_desc *dp; uint32_t ctrl0; uint32_t cache_data_count; + const uint32_t dstart = (uint32_t)data; int dmach; int tl; + int ret = 0; ALLOC_CACHE_ALIGN_BUFFER(struct mxs_dma_desc, desc, desc_count); @@ -239,17 +241,17 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave, if (!write) ctrl0 |= SSP_CTRL0_READ; - writel(length, &ssp_regs->hw_ssp_xfer_size); - if (length % ARCH_DMA_MINALIGN) cache_data_count = roundup(length, ARCH_DMA_MINALIGN); else cache_data_count = length; + /* Flush data to DRAM so DMA can pick them up */ if (write) - /* Flush data to DRAM so DMA can pick them up */ - flush_dcache_range((uint32_t)data, - (uint32_t)(data + cache_data_count)); + flush_dcache_range(dstart, dstart + cache_data_count); + + /* Invalidate the area, so no writeback into the RAM races with DMA */ + invalidate_dcache_range(dstart, dstart + cache_data_count); dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + slave->slave.bus; @@ -281,41 +283,47 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave, tl = min(length, xfer_max_sz); dp->cmd.data |= - (tl << MXS_DMA_DESC_BYTES_OFFSET) | - (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + ((tl & 0xffff) << MXS_DMA_DESC_BYTES_OFFSET) | + (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | MXS_DMA_DESC_HALT_ON_TERMINATE | MXS_DMA_DESC_TERMINATE_FLUSH; - dp->cmd.pio_words[0] = ctrl0; data += tl; length -= tl; + if (!length) { + dp->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; + + if (flags & SPI_XFER_END) { + ctrl0 &= ~SSP_CTRL0_LOCK_CS; + ctrl0 |= SSP_CTRL0_IGNORE_CRC; + } + } + + /* + * Write CTRL0, CMD0, CMD1, XFER_SIZE registers. It is + * essential that the XFER_SIZE register is written on + * a per-descriptor basis with the same size as is the + * descriptor! + */ + dp->cmd.pio_words[0] = ctrl0; + dp->cmd.pio_words[1] = 0; + dp->cmd.pio_words[2] = 0; + dp->cmd.pio_words[3] = tl; + mxs_dma_desc_append(dmach, dp); dp++; } - dp->address = (dma_addr_t)dp; - dp->cmd.address = (dma_addr_t)0; - dp->cmd.data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | - (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | - MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; - if (flags & SPI_XFER_END) { - ctrl0 &= ~SSP_CTRL0_LOCK_CS; - dp->cmd.pio_words[0] = ctrl0 | SSP_CTRL0_IGNORE_CRC; - } - mxs_dma_desc_append(dmach, dp); - if (mxs_dma_go(dmach)) - return -EINVAL; + ret = -EINVAL; /* The data arrived into DRAM, invalidate cache over them */ - if (!write) { - invalidate_dcache_range((uint32_t)data, - (uint32_t)(data + cache_data_count)); - } + if (!write) + invalidate_dcache_range(dstart, dstart + cache_data_count); - return 0; + return ret; } int spi_xfer(struct spi_slave *slave, unsigned int bitlen, diff --git a/drivers/spi/tegra_spi.c b/drivers/spi/tegra_spi.c index 2355e022b..18b00b2ca 100644 --- a/drivers/spi/tegra_spi.c +++ b/drivers/spi/tegra_spi.c @@ -72,9 +72,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, return NULL; } - if (max_hz > TEGRA20_SPI_MAX_FREQ) { + if (max_hz > TEGRA_SPI_MAX_FREQ) { printf("SPI error: unsupported frequency %d Hz. Max frequency" - " is %d Hz\n", max_hz, TEGRA20_SPI_MAX_FREQ); + " is %d Hz\n", max_hz, TEGRA_SPI_MAX_FREQ); return NULL; } @@ -86,7 +86,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, spi->slave.bus = bus; spi->slave.cs = cs; spi->freq = max_hz; - spi->regs = (struct spi_tegra *)TEGRA20_SPI_BASE; + spi->regs = (struct spi_tegra *)NV_PA_SPI_BASE; spi->mode = mode; return &spi->slave; diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index e563c1905..52a4134f1 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -78,7 +78,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int mode) { struct xilinx_spi_slave *xilspi; - struct xilinx_spi_reg *regs; if (!spi_cs_is_valid(bus, cs)) { printf("XILSPI error: %s: unsupported bus %d / cs %d\n", |