diff options
| -rw-r--r-- | doc/SPI/README.sh_qspi_test | 38 | ||||
| -rw-r--r-- | drivers/mtd/spi/sf_ops.c | 6 | ||||
| -rw-r--r-- | drivers/mtd/spi/sf_probe.c | 3 | ||||
| -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 | ||||
| -rw-r--r-- | include/configs/dra7xx_evm.h | 1 | 
7 files changed, 333 insertions, 14 deletions
| diff --git a/doc/SPI/README.sh_qspi_test b/doc/SPI/README.sh_qspi_test new file mode 100644 index 000000000..8a33fec32 --- /dev/null +++ b/doc/SPI/README.sh_qspi_test @@ -0,0 +1,38 @@ +------------------------------------------------- +   Simple steps used to test the SH-QSPI at U-Boot +------------------------------------------------- + +#0, Currently, SH-QSPI is used by lager board (Renesas ARM SoC R8A7790) +    and koelsch board (Renesas ARM SoC R8A7791). These boot from SPI ROM +    basically. Thus, U-Boot start, SH-QSPI will is operating normally. + +#1, build U-Boot and load u-boot.bin + +  => tftpboot 40000000 u-boot.bin +  sh_eth Waiting for PHY auto negotiation to complete.. done +  sh_eth: 100Base/Half +  Using sh_eth device +  TFTP from server 192.168.169.1; our IP address is 192.168.169.79 +  Filename 'u-boot.bin'. +  Load address: 0x40000000 +  Loading: ############ +    2.5 MiB/s +  done +  Bytes transferred = 175364 (2ad04 hex) + +#2, Commands to erase/write u-boot to flash device + +  Note: This method is description of the lager board. If you want to use the +  other boards, please change the value according to each environment. + +  =>  sf probe 0 +  SF: Detected S25FL512S_256K with page size 512 Bytes, erase size 64 KiB, total 64 MiB +  => sf erase 80000 40000 +  SF: 262144 bytes @ 0x80000 Erased: OK +  => sf write 40000000 80000 175364 +  SF: 1528676 bytes @ 0x80000 Written: OK +  => + +#3, Push reset button. + +  If you're written correctly and driver works properly, U-Boot starts. diff --git a/drivers/mtd/spi/sf_ops.c b/drivers/mtd/spi/sf_ops.c index 108665f44..e316a692a 100644 --- a/drivers/mtd/spi/sf_ops.c +++ b/drivers/mtd/spi/sf_ops.c @@ -273,9 +273,15 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,  	/* Handle memory-mapped SPI */  	if (flash->memory_map) { +		ret = spi_claim_bus(flash->spi); +		if (ret) { +			debug("SF: unable to claim SPI bus\n"); +			return ret; +		}  		spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP);  		memcpy(data, flash->memory_map + offset, len);  		spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP_END); +		spi_release_bus(flash->spi);  		return 0;  	} diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index c1eb75489..b863a9828 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -60,6 +60,7 @@ static const struct spi_flash_params spi_flash_params_table[] = {  	{"GD25LQ32",	   0xc86016, 0x0,	64 * 1024,    64,	       SECT_4K},  #endif  #ifdef CONFIG_SPI_FLASH_MACRONIX	/* MACRONIX */ +	{"MX25L2006E",	   0xc22012, 0x0,	64 * 1024,     4,	             0},  	{"MX25L4005",	   0xc22013, 0x0,	64 * 1024,     8,	             0},  	{"MX25L8005",	   0xc22014, 0x0,	64 * 1024,    16,	             0},  	{"MX25L1605D",	   0xc22015, 0x0,	64 * 1024,    32,	             0}, @@ -67,7 +68,7 @@ static const struct spi_flash_params spi_flash_params_table[] = {  	{"MX25L6405D",	   0xc22017, 0x0,	64 * 1024,   128,	             0},  	{"MX25L12805",	   0xc22018, 0x0,	64 * 1024,   256,	             0},  	{"MX25L25635F",	   0xc22019, 0x0,	64 * 1024,   512,	             0}, -	{"MX25L51235F",	   0xc2201A, 0x0,	64 * 1024,  1024,	             0}, +	{"MX25L51235F",	   0xc2201a, 0x0,	64 * 1024,  1024,	             0},  	{"MX25L12855E",	   0xc22618, 0x0,	64 * 1024,   256,	             0},  #endif  #ifdef CONFIG_SPI_FLASH_SPANSION	/* SPANSION */ 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;  			}  		} diff --git a/include/configs/dra7xx_evm.h b/include/configs/dra7xx_evm.h index 48b47cbd0..f210ed8b9 100644 --- a/include/configs/dra7xx_evm.h +++ b/include/configs/dra7xx_evm.h @@ -60,6 +60,7 @@  #define CONFIG_SPI_FLASH_SPANSION  #define CONFIG_CMD_SF  #define CONFIG_CMD_SPI +#define CONFIG_SPI_FLASH_BAR  #define CONFIG_TI_SPI_MMAP  #define CONFIG_SF_DEFAULT_SPEED                48000000  #define CONFIG_DEFAULT_SPI_MODE                SPI_MODE_3 |