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 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/atmel_spi.c b/drivers/spi/atmel_spi.c index 83ef8e8b1..c7a51f7f3 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 8b69a6d21..057de9ade 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 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/mxc_spi.c b/drivers/spi/mxc_spi.c index 2e1531858..13bebe8ac 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 7859536a6..42e4c9952 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 9346c0b5b..e40a632ca 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 0ac801cb2..bffa43cb6 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 4a3e7996f..18b00b2ca 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 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", |