diff options
Diffstat (limited to 'drivers/spi')
| -rw-r--r-- | drivers/spi/Makefile | 1 | ||||
| -rw-r--r-- | drivers/spi/sh_qspi.c | 277 | ||||
| -rw-r--r-- | drivers/spi/tegra114_spi.c | 21 | 
3 files changed, 286 insertions, 13 deletions
| diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index ed4ecd754..d5a7143b5 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o  obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o  obj-$(CONFIG_SOFT_SPI) += soft_spi.o  obj-$(CONFIG_SH_SPI) += sh_spi.o +obj-$(CONFIG_SH_QSPI) += sh_qspi.o  obj-$(CONFIG_FSL_ESPI) += fsl_espi.o  obj-$(CONFIG_FDT_SPI) += fdt_spi.o  obj-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o diff --git a/drivers/spi/sh_qspi.c b/drivers/spi/sh_qspi.c new file mode 100644 index 000000000..edeb42d03 --- /dev/null +++ b/drivers/spi/sh_qspi.c @@ -0,0 +1,277 @@ +/* + * SH QSPI (Quad SPI) driver + * + * Copyright (C) 2013 Renesas Electronics Corporation + * Copyright (C) 2013 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> + * + * SPDX-License-Identifier:	GPL-2.0 + */ + +#include <common.h> +#include <malloc.h> +#include <spi.h> +#include <asm/io.h> + +/* SH QSPI register bit masks <REG>_<BIT> */ +#define SPCR_MSTR	0x08 +#define SPCR_SPE	0x40 +#define SPSR_SPRFF	0x80 +#define SPSR_SPTEF	0x20 +#define SPPCR_IO3FV	0x04 +#define SPPCR_IO2FV	0x02 +#define SPPCR_IO1FV	0x01 +#define SPBDCR_RXBC0	(1 << 0) +#define SPCMD_SCKDEN	(1 << 15) +#define SPCMD_SLNDEN	(1 << 14) +#define SPCMD_SPNDEN	(1 << 13) +#define SPCMD_SSLKP	(1 << 7) +#define SPCMD_BRDV0	(1 << 2) +#define SPCMD_INIT1	SPCMD_SCKDEN | SPCMD_SLNDEN | \ +			SPCMD_SPNDEN | SPCMD_SSLKP | \ +			SPCMD_BRDV0 +#define SPCMD_INIT2	SPCMD_SPNDEN | SPCMD_SSLKP | \ +			SPCMD_BRDV0 +#define SPBFCR_TXRST	(1 << 7) +#define SPBFCR_RXRST	(1 << 6) + +/* SH QSPI register set */ +struct sh_qspi_regs { +	unsigned char spcr; +	unsigned char sslp; +	unsigned char sppcr; +	unsigned char spsr; +	unsigned long spdr; +	unsigned char spscr; +	unsigned char spssr; +	unsigned char spbr; +	unsigned char spdcr; +	unsigned char spckd; +	unsigned char sslnd; +	unsigned char spnd; +	unsigned char dummy0; +	unsigned short spcmd0; +	unsigned short spcmd1; +	unsigned short spcmd2; +	unsigned short spcmd3; +	unsigned char spbfcr; +	unsigned char dummy1; +	unsigned short spbdcr; +	unsigned long spbmul0; +	unsigned long spbmul1; +	unsigned long spbmul2; +	unsigned long spbmul3; +}; + +struct sh_qspi_slave { +	struct spi_slave	slave; +	struct sh_qspi_regs	*regs; +}; + +static inline struct sh_qspi_slave *to_sh_qspi(struct spi_slave *slave) +{ +	return container_of(slave, struct sh_qspi_slave, slave); +} + +static void sh_qspi_init(struct sh_qspi_slave *ss) +{ +	/* QSPI initialize */ +	/* Set master mode only */ +	writeb(SPCR_MSTR, &ss->regs->spcr); + +	/* Set SSL signal level */ +	writeb(0x00, &ss->regs->sslp); + +	/* Set MOSI signal value when transfer is in idle state */ +	writeb(SPPCR_IO3FV|SPPCR_IO2FV, &ss->regs->sppcr); + +	/* Set bit rate. See 58.3.8 Quad Serial Peripheral Interface */ +	writeb(0x01, &ss->regs->spbr); + +	/* Disable Dummy Data Transmission */ +	writeb(0x00, &ss->regs->spdcr); + +	/* Set clock delay value */ +	writeb(0x00, &ss->regs->spckd); + +	/* Set SSL negation delay value */ +	writeb(0x00, &ss->regs->sslnd); + +	/* Set next-access delay value */ +	writeb(0x00, &ss->regs->spnd); + +	/* Set equence command */ +	writew(SPCMD_INIT2, &ss->regs->spcmd0); + +	/* Reset transfer and receive Buffer */ +	setbits_8(&ss->regs->spbfcr, SPBFCR_TXRST|SPBFCR_RXRST); + +	/* Clear transfer and receive Buffer control bit */ +	clrbits_8(&ss->regs->spbfcr, SPBFCR_TXRST|SPBFCR_RXRST); + +	/* Set equence control method. Use equence0 only */ +	writeb(0x00, &ss->regs->spscr); + +	/* Enable SPI function */ +	setbits_8(&ss->regs->spcr, SPCR_SPE); +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ +	return 1; +} + +void spi_cs_activate(struct spi_slave *slave) +{ +	struct sh_qspi_slave *ss = to_sh_qspi(slave); + +	/* Set master mode only */ +	writeb(SPCR_MSTR, &ss->regs->spcr); + +	/* Set command */ +	writew(SPCMD_INIT1, &ss->regs->spcmd0); + +	/* Reset transfer and receive Buffer */ +	setbits_8(&ss->regs->spbfcr, SPBFCR_TXRST|SPBFCR_RXRST); + +	/* Clear transfer and receive Buffer control bit */ +	clrbits_8(&ss->regs->spbfcr, SPBFCR_TXRST|SPBFCR_RXRST); + +	/* Set equence control method. Use equence0 only */ +	writeb(0x00, &ss->regs->spscr); + +	/* Enable SPI function */ +	setbits_8(&ss->regs->spcr, SPCR_SPE); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ +	struct sh_qspi_slave *ss = to_sh_qspi(slave); + +	/* Disable SPI Function */ +	clrbits_8(&ss->regs->spcr, SPCR_SPE); +} + +void spi_init(void) +{ +	/* nothing to do */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, +		unsigned int max_hz, unsigned int mode) +{ +	struct sh_qspi_slave *ss; + +	if (!spi_cs_is_valid(bus, cs)) +		return NULL; + +	ss = spi_alloc_slave(struct sh_qspi_slave, bus, cs); +	if (!ss) { +		printf("SPI_error: Fail to allocate sh_qspi_slave\n"); +		return NULL; +	} + +	ss->regs = (struct sh_qspi_regs *)CONFIG_SH_QSPI_BASE; + +	/* Init SH QSPI */ +	sh_qspi_init(ss); + +	return &ss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ +	struct sh_qspi_slave *spi = to_sh_qspi(slave); + +	free(spi); +} + +int spi_claim_bus(struct spi_slave *slave) +{ +	return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, +	     void *din, unsigned long flags) +{ +	struct sh_qspi_slave *ss = to_sh_qspi(slave); +	unsigned long nbyte; +	int ret = 0; +	unsigned char dtdata = 0, drdata; +	unsigned char *tdata = &dtdata, *rdata = &drdata; +	unsigned long *spbmul0 = &ss->regs->spbmul0; + +	if (dout == NULL && din == NULL) { +		if (flags & SPI_XFER_END) +			spi_cs_deactivate(slave); +		return 0; +	} + +	if (bitlen % 8) { +		printf("%s: bitlen is not 8bit alined %d", __func__, bitlen); +		return 1; +	} + +	nbyte = bitlen / 8; + +	if (flags & SPI_XFER_BEGIN) { +		spi_cs_activate(slave); + +		/* Set 1048576 byte */ +		writel(0x100000, spbmul0); +	} + +	if (flags & SPI_XFER_END) +		writel(nbyte, spbmul0); + +	if (dout != NULL) +		tdata = (unsigned char *)dout; + +	if (din != NULL) +		rdata = din; + +	while (nbyte > 0) { +		while (!(readb(&ss->regs->spsr) & SPSR_SPTEF)) { +			if (ctrlc()) { +				puts("abort\n"); +				return 1; +			} +			udelay(10); +		} + +		writeb(*tdata, (unsigned char *)(&ss->regs->spdr)); + +		while ((readw(&ss->regs->spbdcr) != SPBDCR_RXBC0)) { +			if (ctrlc()) { +				puts("abort\n"); +				return 1; +			} +			udelay(1); +		} + +		while (!(readb(&ss->regs->spsr) & SPSR_SPRFF)) { +			if (ctrlc()) { +				puts("abort\n"); +				return 1; +			} +			udelay(10); +		} + +		*rdata = readb((unsigned char *)(&ss->regs->spdr)); + +		if (dout != NULL) +			tdata++; +		if (din != NULL) +			rdata++; + +		nbyte--; +	} + +	if (flags & SPI_XFER_END) +		spi_cs_deactivate(slave); + +	return ret; +} diff --git a/drivers/spi/tegra114_spi.c b/drivers/spi/tegra114_spi.c index 4d2af483d..810fa4718 100644 --- a/drivers/spi/tegra114_spi.c +++ b/drivers/spi/tegra114_spi.c @@ -289,9 +289,6 @@ int tegra114_spi_xfer(struct spi_slave *slave, unsigned int bitlen,  	reg = readl(®s->fifo_status);  	writel(reg, ®s->fifo_status); -	/* clear ready bit */ -	setbits_le32(®s->xfer_status, SPI_XFER_STS_RDY); -  	clrsetbits_le32(®s->command1, SPI_CMD1_CS_SW_VAL,  			SPI_CMD1_RX_EN | SPI_CMD1_TX_EN | SPI_CMD1_LSBY_FE |  			(slave->cs << SPI_CMD1_CS_SEL_SHIFT)); @@ -305,7 +302,6 @@ int tegra114_spi_xfer(struct spi_slave *slave, unsigned int bitlen,  	/* handle data in 32-bit chunks */  	while (num_bytes > 0) {  		int bytes; -		int is_read = 0;  		int tm, i;  		tmpdout = 0; @@ -319,6 +315,9 @@ int tegra114_spi_xfer(struct spi_slave *slave, unsigned int bitlen,  		num_bytes -= bytes; +		/* clear ready bit */ +		setbits_le32(®s->xfer_status, SPI_XFER_STS_RDY); +  		clrsetbits_le32(®s->command1,  				SPI_CMD1_BIT_LEN_MASK << SPI_CMD1_BIT_LEN_SHIFT,  				(bytes * 8 - 1) << SPI_CMD1_BIT_LEN_SHIFT); @@ -329,20 +328,14 @@ int tegra114_spi_xfer(struct spi_slave *slave, unsigned int bitlen,  		 * Wait for SPI transmit FIFO to empty, or to time out.  		 * The RX FIFO status will be read and cleared last  		 */ -		for (tm = 0, is_read = 0; tm < SPI_TIMEOUT; ++tm) { +		for (tm = 0; tm < SPI_TIMEOUT; ++tm) {  			u32 fifo_status, xfer_status; -			fifo_status = readl(®s->fifo_status); - -			/* We can exit when we've had both RX and TX activity */ -			if (is_read && -			    (fifo_status & SPI_FIFO_STS_TX_FIFO_EMPTY)) -				break; -  			xfer_status = readl(®s->xfer_status);  			if (!(xfer_status & SPI_XFER_STS_RDY))  				continue; +			fifo_status = readl(®s->fifo_status);  			if (fifo_status & SPI_FIFO_STS_ERR) {  				debug("%s: got a fifo error: ", __func__);  				if (fifo_status & SPI_FIFO_STS_TX_FIFO_OVF) @@ -367,7 +360,6 @@ int tegra114_spi_xfer(struct spi_slave *slave, unsigned int bitlen,  			if (!(fifo_status & SPI_FIFO_STS_RX_FIFO_EMPTY)) {  				tmpdin = readl(®s->rx_fifo); -				is_read = 1;  				/* swap bytes read in */  				if (din != NULL) { @@ -377,6 +369,9 @@ int tegra114_spi_xfer(struct spi_slave *slave, unsigned int bitlen,  					}  					din += bytes;  				} + +				/* We can exit when we've had both RX and TX */ +				break;  			}  		} |