diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/atmel_spi.c | 3 | ||||
-rw-r--r-- | drivers/spi/atmel_spi.h | 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/mxc_spi.c | 6 | ||||
-rw-r--r-- | drivers/spi/mxs_spi.c | 252 | ||||
-rw-r--r-- | drivers/spi/omap3_spi.c | 16 | ||||
-rw-r--r-- | drivers/spi/omap3_spi.h | 5 | ||||
-rw-r--r-- | drivers/spi/tegra_spi.c | 10 | ||||
-rw-r--r-- | drivers/spi/xilinx_spi.c | 1 |
12 files changed, 628 insertions, 49 deletions
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 80b981f10f..f0b82c67f5 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/atmel_spi.c b/drivers/spi/atmel_spi.c index 83ef8e8b19..c7a51f7f39 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -92,6 +92,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, as->slave.cs = cs; as->regs = regs; as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS +#if defined(CONFIG_AT91SAM9X5) + | ATMEL_SPI_MR_WDRBT +#endif | ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf); spi_writel(as, CSR(cs), csrx); diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h index 8b69a6d215..057de9adee 100644 --- a/drivers/spi/atmel_spi.h +++ b/drivers/spi/atmel_spi.h @@ -26,6 +26,7 @@ #define ATMEL_SPI_MR_PCSDEC (1 << 2) #define ATMEL_SPI_MR_FDIV (1 << 3) #define ATMEL_SPI_MR_MODFDIS (1 << 4) +#define ATMEL_SPI_MR_WDRBT (1 << 5) #define ATMEL_SPI_MR_LLB (1 << 7) #define ATMEL_SPI_MR_PCS(x) (((x) & 15) << 16) #define ATMEL_SPI_MR_DLYBCS(x) ((x) << 24) diff --git a/drivers/spi/cf_qspi.c b/drivers/spi/cf_qspi.c new file mode 100644 index 0000000000..72dd1a520d --- /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 f4523a3929..a7cda751bd 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 44ab39dd3f..4e46041dff 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/mxc_spi.c b/drivers/spi/mxc_spi.c index 2e1531858d..13bebe8ac1 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -96,7 +96,7 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs, clk_src = mxc_get_clock(MXC_CSPI_CLK); - div = clk_src / max_hz; + div = DIV_ROUND_UP(clk_src, max_hz); div = get_cspi_div(div); debug("clk %d Hz, div %d, real clk %d Hz\n", @@ -147,7 +147,7 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs, * The following computation is taken directly from Freescale's code. */ if (clk_src > max_hz) { - pre_div = clk_src / max_hz; + pre_div = DIV_ROUND_UP(clk_src, max_hz); if (pre_div > 16) { post_div = pre_div / 16; pre_div = 15; @@ -408,7 +408,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, if (bus >= ARRAY_SIZE(spi_bases)) return NULL; - mxcs = malloc(sizeof(struct mxc_spi_slave)); + mxcs = calloc(sizeof(struct mxc_spi_slave), 1); if (!mxcs) { puts("mxc_spi: SPI Slave not allocated !\n"); return NULL; diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c index 7859536a67..42e4c9952e 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -31,17 +31,31 @@ #include <asm/arch/clock.h> #include <asm/arch/imx-regs.h> #include <asm/arch/sys_proto.h> +#include <asm/arch/dma.h> #define MXS_SPI_MAX_TIMEOUT 1000000 #define MXS_SPI_PORT_OFFSET 0x2000 #define MXS_SSP_CHIPSELECT_MASK 0x00300000 #define MXS_SSP_CHIPSELECT_SHIFT 20 +#define MXSSSP_SMALL_TRANSFER 512 + +/* + * CONFIG_MXS_SPI_DMA_ENABLE: Experimental mixed PIO/DMA support for MXS SPI + * host. Use with utmost caution! + * + * Enabling this is not yet recommended since this + * still doesn't support transfers to/from unaligned + * addresses. Therefore this driver will not work + * for example with saving environment. This is + * caused by DMA alignment constraints on MXS. + */ + struct mxs_spi_slave { struct spi_slave slave; uint32_t max_khz; uint32_t mode; - struct mx28_ssp_regs *regs; + struct mxs_ssp_regs *regs; }; static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave) @@ -67,7 +81,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, { struct mxs_spi_slave *mxs_slave; uint32_t addr; - struct mx28_ssp_regs *ssp_regs; + struct mxs_ssp_regs *ssp_regs; int reg; if (!spi_cs_is_valid(bus, cs)) { @@ -75,17 +89,20 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, return NULL; } - mxs_slave = malloc(sizeof(struct mxs_spi_slave)); + mxs_slave = calloc(sizeof(struct mxs_spi_slave), 1); if (!mxs_slave) return NULL; + if (mxs_dma_init_channel(bus)) + goto err_init; + addr = MXS_SSP0_BASE + (bus * MXS_SPI_PORT_OFFSET); mxs_slave->slave.bus = bus; mxs_slave->slave.cs = cs; mxs_slave->max_khz = max_hz / 1000; mxs_slave->mode = mode; - mxs_slave->regs = (struct mx28_ssp_regs *)addr; + mxs_slave->regs = (struct mxs_ssp_regs *)addr; ssp_regs = mxs_slave->regs; reg = readl(&ssp_regs->hw_ssp_ctrl0); @@ -94,6 +111,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, writel(reg, &ssp_regs->hw_ssp_ctrl0); return &mxs_slave->slave; + +err_init: + free(mxs_slave); + return NULL; } void spi_free_slave(struct spi_slave *slave) @@ -105,10 +126,10 @@ void spi_free_slave(struct spi_slave *slave) int spi_claim_bus(struct spi_slave *slave) { struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); - struct mx28_ssp_regs *ssp_regs = mxs_slave->regs; + struct mxs_ssp_regs *ssp_regs = mxs_slave->regs; uint32_t reg = 0; - mx28_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); + mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); writel(SSP_CTRL0_BUS_WIDTH_ONE_BIT, &ssp_regs->hw_ssp_ctrl0); @@ -128,79 +149,63 @@ void spi_release_bus(struct spi_slave *slave) { } -static void mxs_spi_start_xfer(struct mx28_ssp_regs *ssp_regs) +static void mxs_spi_start_xfer(struct mxs_ssp_regs *ssp_regs) { writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_set); writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_clr); } -static void mxs_spi_end_xfer(struct mx28_ssp_regs *ssp_regs) +static void mxs_spi_end_xfer(struct mxs_ssp_regs *ssp_regs) { writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_clr); writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_set); } -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, - const void *dout, void *din, unsigned long flags) +static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave, + char *data, int length, int write, unsigned long flags) { - struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); - struct mx28_ssp_regs *ssp_regs = mxs_slave->regs; - int len = bitlen / 8; - const char *tx = dout; - char *rx = din; - char dummy; - - if (bitlen == 0) { - if (flags & SPI_XFER_END) { - rx = &dummy; - len = 1; - } else - return 0; - } - - if (!rx && !tx) - return 0; + struct mxs_ssp_regs *ssp_regs = slave->regs; if (flags & SPI_XFER_BEGIN) mxs_spi_start_xfer(ssp_regs); - while (len--) { + while (length--) { /* We transfer 1 byte */ writel(1, &ssp_regs->hw_ssp_xfer_size); - if ((flags & SPI_XFER_END) && !len) + if ((flags & SPI_XFER_END) && !length) mxs_spi_end_xfer(ssp_regs); - if (tx) + if (write) writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_clr); else writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_set); writel(SSP_CTRL0_RUN, &ssp_regs->hw_ssp_ctrl0_set); - if (mx28_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg, + if (mxs_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg, SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) { printf("MXS SPI: Timeout waiting for start\n"); return -ETIMEDOUT; } - if (tx) - writel(*tx++, &ssp_regs->hw_ssp_data); + if (write) + writel(*data++, &ssp_regs->hw_ssp_data); writel(SSP_CTRL0_DATA_XFER, &ssp_regs->hw_ssp_ctrl0_set); - if (rx) { - if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_status_reg, + if (!write) { + if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_status_reg, SSP_STATUS_FIFO_EMPTY, MXS_SPI_MAX_TIMEOUT)) { printf("MXS SPI: Timeout waiting for data\n"); return -ETIMEDOUT; } - *rx = readl(&ssp_regs->hw_ssp_data); - rx++; + *data = readl(&ssp_regs->hw_ssp_data); + data++; } - if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg, + if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg, SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) { printf("MXS SPI: Timeout waiting for finish\n"); return -ETIMEDOUT; @@ -209,3 +214,174 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, return 0; } + +static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave, + char *data, int length, int write, unsigned long flags) +{ + const int xfer_max_sz = 0xff00; + const int desc_count = DIV_ROUND_UP(length, xfer_max_sz) + 1; + struct mxs_ssp_regs *ssp_regs = slave->regs; + 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); + + memset(desc, 0, sizeof(struct mxs_dma_desc) * desc_count); + + ctrl0 = readl(&ssp_regs->hw_ssp_ctrl0); + ctrl0 |= SSP_CTRL0_DATA_XFER; + + if (flags & SPI_XFER_BEGIN) + ctrl0 |= SSP_CTRL0_LOCK_CS; + if (!write) + ctrl0 |= SSP_CTRL0_READ; + + 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_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; + + dp = desc; + while (length) { + dp->address = (dma_addr_t)dp; + dp->cmd.address = (dma_addr_t)data; + + /* + * This is correct, even though it does indeed look insane. + * I hereby have to, wholeheartedly, thank Freescale Inc., + * for always inventing insane hardware and keeping me busy + * and employed ;-) + */ + if (write) + dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ; + else + dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE; + + /* + * The DMA controller can transfer large chunks (64kB) at + * time by setting the transfer length to 0. Setting tl to + * 0x10000 will overflow below and make .data contain 0. + * Otherwise, 0xff00 is the transfer maximum. + */ + if (length >= 0x10000) + tl = 0x10000; + else + tl = min(length, xfer_max_sz); + + dp->cmd.data |= + ((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; + + 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++; + } + + if (mxs_dma_go(dmach)) + ret = -EINVAL; + + /* The data arrived into DRAM, invalidate cache over them */ + if (!write) + invalidate_dcache_range(dstart, dstart + cache_data_count); + + return ret; +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); + struct mxs_ssp_regs *ssp_regs = mxs_slave->regs; + int len = bitlen / 8; + char dummy; + int write = 0; + char *data = NULL; + +#ifdef CONFIG_MXS_SPI_DMA_ENABLE + int dma = 1; +#else + int dma = 0; +#endif + + if (bitlen == 0) { + if (flags & SPI_XFER_END) { + din = (void *)&dummy; + len = 1; + } else + return 0; + } + + /* Half-duplex only */ + if (din && dout) + return -EINVAL; + /* No data */ + if (!din && !dout) + return 0; + + if (dout) { + data = (char *)dout; + write = 1; + } else if (din) { + data = (char *)din; + write = 0; + } + + /* + * Check for alignment, if the buffer is aligned, do DMA transfer, + * PIO otherwise. This is a temporary workaround until proper bounce + * buffer is in place. + */ + if (dma) { + if (((uint32_t)data) & (ARCH_DMA_MINALIGN - 1)) + dma = 0; + if (((uint32_t)len) & (ARCH_DMA_MINALIGN - 1)) + dma = 0; + } + + if (!dma || (len < MXSSSP_SMALL_TRANSFER)) { + writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr); + return mxs_spi_xfer_pio(mxs_slave, data, len, write, flags); + } else { + writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set); + return mxs_spi_xfer_dma(mxs_slave, data, len, write, flags); + } +} diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c index 9346c0b5b4..e40a632caa 100644 --- a/drivers/spi/omap3_spi.c +++ b/drivers/spi/omap3_spi.c @@ -86,15 +86,21 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, case 0: ds->regs = (struct mcspi *)OMAP3_MCSPI1_BASE; break; +#ifdef OMAP3_MCSPI2_BASE case 1: ds->regs = (struct mcspi *)OMAP3_MCSPI2_BASE; break; +#endif +#ifdef OMAP3_MCSPI3_BASE case 2: ds->regs = (struct mcspi *)OMAP3_MCSPI3_BASE; break; +#endif +#ifdef OMAP3_MCSPI4_BASE case 3: ds->regs = (struct mcspi *)OMAP3_MCSPI4_BASE; break; +#endif default: printf("SPI error: unsupported bus %i. \ Supported busses 0 - 3\n", bus); @@ -167,8 +173,18 @@ int spi_claim_bus(struct spi_slave *slave) /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS * REVISIT: this controller could support SPI_3WIRE mode. */ +#ifdef CONFIG_AM33XX + /* + * The reference design on AM33xx has D0 and D1 wired up opposite + * of how it has been done on previous platforms. We assume that + * custom hardware will also follow this convention. + */ + conf &= OMAP3_MCSPI_CHCONF_DPE0; + conf |= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1); +#else conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1); conf |= OMAP3_MCSPI_CHCONF_DPE0; +#endif /* wordlength */ conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK; diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h index 0ac801cb25..bffa43cb6c 100644 --- a/drivers/spi/omap3_spi.h +++ b/drivers/spi/omap3_spi.h @@ -30,10 +30,15 @@ #ifndef _OMAP3_SPI_H_ #define _OMAP3_SPI_H_ +#ifdef CONFIG_AM33XX +#define OMAP3_MCSPI1_BASE 0x48030100 +#define OMAP3_MCSPI2_BASE 0x481A0100 +#else #define OMAP3_MCSPI1_BASE 0x48098000 #define OMAP3_MCSPI2_BASE 0x4809A000 #define OMAP3_MCSPI3_BASE 0x480B8000 #define OMAP3_MCSPI4_BASE 0x480BA000 +#endif #define OMAP3_MCSPI_MAX_FREQ 48000000 diff --git a/drivers/spi/tegra_spi.c b/drivers/spi/tegra_spi.c index 4a3e7996f9..18b00b2cae 100644 --- a/drivers/spi/tegra_spi.c +++ b/drivers/spi/tegra_spi.c @@ -54,7 +54,7 @@ static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave) int spi_cs_is_valid(unsigned int bus, unsigned int cs) { - /* Tegra2 SPI-Flash - only 1 device ('bus/cs') */ + /* Tegra20 SPI-Flash - only 1 device ('bus/cs') */ if (bus != 0 || cs != 0) return 0; else @@ -72,9 +72,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, return NULL; } - if (max_hz > TEGRA2_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, TEGRA2_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 *)TEGRA2_SPI_BASE; + spi->regs = (struct spi_tegra *)NV_PA_SPI_BASE; spi->mode = mode; return &spi->slave; @@ -130,7 +130,7 @@ int spi_claim_bus(struct spi_slave *slave) debug("spi_init: COMMAND = %08x\n", readl(®s->command)); /* - * SPI pins on Tegra2 are muxed - change pinmux later due to UART + * SPI pins on Tegra20 are muxed - change pinmux later due to UART * issue. */ pinmux_set_func(PINGRP_GMD, PMUX_FUNC_SFLASH); diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index e563c19051..52a4134f18 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", |