spi: Add support SH Quad SPI driver
This patch adds a driver for Renesas SoC's Quad SPI bus. This supports with 8 bits per transfer to use with SPI flash. Signed-off-by: Kouei Abe <kouei.abe.cp@renesas.com> Signed-off-by: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
This commit is contained in:
		
							parent
							
								
									57af475389
								
							
						
					
					
						commit
						16f47c9c51
					
				|  | @ -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. | ||||||
|  | @ -30,6 +30,7 @@ obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o | ||||||
| obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o | obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o | ||||||
| obj-$(CONFIG_SOFT_SPI) += soft_spi.o | obj-$(CONFIG_SOFT_SPI) += soft_spi.o | ||||||
| obj-$(CONFIG_SH_SPI) += sh_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_FSL_ESPI) += fsl_espi.o | ||||||
| obj-$(CONFIG_FDT_SPI) += fdt_spi.o | obj-$(CONFIG_FDT_SPI) += fdt_spi.o | ||||||
| obj-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o | obj-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue