Patchwork [U-Boot,1/5] sf: Add support for accessing dual parallel memories

login
register
mail settings
Submitter Jagannadha Sutradharudu Teki
Date July 29, 2013, 3:23 p.m.
Message ID <cb1c7100-662f-4c15-a48b-413111b6da3f@AM1EHSMHS015.ehs.local>
Download mbox | patch
Permalink /patch/262840/
State Superseded
Delegated to: Jagannadha Sutradharudu Teki
Headers show

Comments

Jagannadha Sutradharudu Teki - July 29, 2013, 3:23 p.m.
This patch added support for accessing dual memories in
parallel connections with single chipselect line from controller.
this connection mode is implemented in xilinx zynq qspi controller.

For more info, see doc/README.spi-flash-conn-modes

Below are the changes for dual parallel to work:
- mtd layer -> addr/2, page_size*2, sector_size*2
- driver -> enable SEP_BUS[BIT:29],TWO_MEM[BIT:30] on LQSPI_CFG reg.

Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
---
 doc/README.spi-flash-conn-modes | 68 +++++++++++++++++++++++++++++++++++++++++
 drivers/mtd/spi/spi_flash.c     | 52 ++++++++++++++++++++++---------
 include/spi.h                   |  2 ++
 include/spi_flash.h             |  7 +++++
 4 files changed, 114 insertions(+), 15 deletions(-)
 create mode 100644 doc/README.spi-flash-conn-modes

Patch

diff --git a/doc/README.spi-flash-conn-modes b/doc/README.spi-flash-conn-modes
new file mode 100644
index 0000000..f0c94b7
--- /dev/null
+++ b/doc/README.spi-flash-conn-modes
@@ -0,0 +1,68 @@ 
+#
+# (C) Copyright 2013 Xilinx, Inc.
+#
+#  SPDX-License-Identifier:      GPL-2.0+
+
+SPI/QSPI flash connection modes:
+================================
+
+This describes how SPI/QSPI flash memories are connected to a given
+controller in a single chipselect line.
+
+Current spi_flash framework supports, single flash memory connected
+to a given controller with single chipselect line, but there are some
+hw logics(ex: xilinx zynq qspi) that describes two/dual memories are
+connected with a single chipselect line from a controller.
+
+"is_dual" from include/spi.h decribes these types of connection modes.
+
+Possible connections:
+--------------------
+SPI_FLASH_CONN_UNKNOWN:
+SPI_FLASH_CONN_SINGLE:
+	- single spi flash memory connected with single chip select line.
+
+  +-------------+           CS		+---------------+
+  |		|---------------------->|		|
+  | Controller	|        I0[3:0]	| Flash memory	|
+  | SPI/QSPI	|<=====================>| (SPI/QSPI)	|
+  |		|	   CLK		|		|
+  |		|---------------------->|		|
+  +-------------+			+---------------+
+
+SPI_FLASH_CONN_DUALPARALLEL:
+	- dual spi/qspi flash memories are connected with a single chipselect
+	  line and these two memories are operating parallel with separate buses.
+	- xilinx zynq qspi controller has implemented this feature.
+	  see trm for more info
+	  http://www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-TRM.pdf
+
+  +-------------+           CS		+---------------+
+  |		|---------------------->|		|
+  | 		|        I0[3:0]	| Upper Flash	|
+  | 		|<=====================>| memory	|
+  |		|	   CLK		| (SPI/QSPI)	|
+  |		|---------------------->|		|
+  | Controller	|	    CS		+---------------+
+  | SPI/QSPI	|---------------------->|		|
+  | 		|        I0[3:0]	| Lower Flash	|
+  | 		|<=====================>| memory	|
+  |		|	   CLK		| (SPI/QSPI)	|
+  |		|---------------------->|		|
+  +-------------+			+---------------+
+
+	- two memory flash devices should has same hw part attributes (like size,
+	  vendor..etc)
+	- Configurations:
+		Need to enable SEP_BUS[BIT:29],TWO_MEM[BIT:30] on LQSPI_CFG register.
+	- Operation:
+		Even bits, i.e. bit 0, 2, 4 ., of a data word is located in the lower memory
+		and odd bits, i.e. bit 1, 3, 5, ., of a data word is located in the upper memory.
+
+Note: Technically there is only one CS line from the controller, but
+zynq qspi controller has an internal hw logic to enable additional CS
+when controller is configured for dual memories.
+
+--
+Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
+29-07-2013.
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index 6a6fe37..3ed90bd 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -156,9 +156,10 @@  int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
 
 int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)
 {
-	u32 erase_size;
+	u32 erase_size, erase_addr;
 	u8 cmd[4];
 	int ret = -1;
+	int is_dual = flash->spi->is_dual;
 
 	erase_size = flash->sector_size;
 	if (offset % erase_size || len % erase_size) {
@@ -172,10 +173,13 @@  int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)
 		cmd[0] = CMD_ERASE_64K;
 
 	while (len) {
+		erase_addr = offset;
+		if (is_dual == SPI_FLASH_CONN_DUALPARALLEL)
+			erase_addr /= 2;
 #ifdef CONFIG_SPI_FLASH_BAR
 		u8 bank_sel;
 
-		bank_sel = offset / SPI_FLASH_16MB_BOUN;
+		bank_sel = erase_addr / SPI_FLASH_16MB_BOUN;
 
 		ret = spi_flash_cmd_bankaddr_write(flash, bank_sel);
 		if (ret) {
@@ -183,10 +187,10 @@  int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)
 			return ret;
 		}
 #endif
-		spi_flash_addr(offset, cmd);
+		spi_flash_addr(erase_addr, cmd);
 
 		debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1],
-		      cmd[2], cmd[3], offset);
+		      cmd[2], cmd[3], erase_addr);
 
 		ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0);
 		if (ret < 0) {
@@ -204,19 +208,23 @@  int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)
 int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
 		size_t len, const void *buf)
 {
-	unsigned long byte_addr, page_size;
+	unsigned long byte_addr, page_size, write_addr;
 	size_t chunk_len, actual;
 	u8 cmd[4];
 	int ret = -1;
+	int is_dual = flash->spi->is_dual;
 
 	page_size = flash->page_size;
 
 	cmd[0] = CMD_PAGE_PROGRAM;
 	for (actual = 0; actual < len; actual += chunk_len) {
+		write_addr = offset;
+		if (is_dual == SPI_FLASH_CONN_DUALPARALLEL)
+			write_addr /= 2;
 #ifdef CONFIG_SPI_FLASH_BAR
 		u8 bank_sel;
 
-		bank_sel = offset / SPI_FLASH_16MB_BOUN;
+		bank_sel = write_addr / SPI_FLASH_16MB_BOUN;
 
 		ret = spi_flash_cmd_bankaddr_write(flash, bank_sel);
 		if (ret) {
@@ -230,7 +238,7 @@  int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
 		if (flash->spi->max_write_size)
 			chunk_len = min(chunk_len, flash->spi->max_write_size);
 
-		spi_flash_addr(offset, cmd);
+		spi_flash_addr(write_addr, cmd);
 
 		debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n",
 		      buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
@@ -275,8 +283,9 @@  int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
 		size_t len, void *data)
 {
 	u8 cmd[5], bank_sel = 0;
-	u32 remain_len, read_len;
+	u32 remain_len, read_len, read_addr, bank_boun;
 	int ret = -1;
+	int is_dual = flash->spi->is_dual;
 
 	/* Handle memory-mapped SPI */
 	if (flash->memory_map) {
@@ -284,12 +293,19 @@  int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
 		return 0;
 	}
 
+	bank_boun = SPI_FLASH_16MB_BOUN;
+	if (is_dual == SPI_FLASH_CONN_DUALPARALLEL)
+		bank_boun = SPI_FLASH_16MB_BOUN << 1;
+
 	cmd[0] = CMD_READ_ARRAY_FAST;
 	cmd[4] = 0x00;
 
 	while (len) {
+		read_addr = offset;
+		if (is_dual == SPI_FLASH_CONN_DUALPARALLEL)
+			read_addr /= 2;
 #ifdef CONFIG_SPI_FLASH_BAR
-		bank_sel = offset / SPI_FLASH_16MB_BOUN;
+		bank_sel = read_addr / SPI_FLASH_16MB_BOUN;
 
 		ret = spi_flash_cmd_bankaddr_write(flash, bank_sel);
 		if (ret) {
@@ -297,13 +313,13 @@  int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
 			return ret;
 		}
 #endif
-		remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1) - offset);
+		remain_len = (bank_boun * (bank_sel + 1) - read_addr);
 		if (len < remain_len)
 			read_len = len;
 		else
 			read_len = remain_len;
 
-		spi_flash_addr(offset, cmd);
+		spi_flash_addr(read_addr, cmd);
 
 		ret = spi_flash_read_common(flash, cmd, sizeof(cmd),
 							data, read_len);
@@ -380,7 +396,10 @@  int spi_flash_bank_config(struct spi_flash *flash, u8 idcode0)
 
 	/* read the bank reg - on which bank the flash is in currently */
 	cmd = flash->bank_read_cmd;
-	if (flash->size > SPI_FLASH_16MB_BOUN) {
+	if (((flash->spi->is_dual == SPI_FLASH_CONN_SINGLE) &&
+			(flash->size > SPI_FLASH_16MB_BOUN)) ||
+			((flash->spi->is_dual == SPI_FLASH_CONN_DUALPARALLEL) &&
+			((flash->size / 2) > SPI_FLASH_16MB_BOUN))) {
 		if (spi_flash_read_common(flash, &cmd, 1, &curr_bank, 1)) {
 			debug("SF: fail to read bank addr register\n");
 			return -1;
@@ -561,9 +580,12 @@  struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
 		printf(", mapped at %p", flash->memory_map);
 	puts("\n");
 #ifndef CONFIG_SPI_FLASH_BAR
-	if (flash->size > SPI_FLASH_16MB_BOUN) {
-		puts("SF: Warning - Only lower 16MiB accessible,");
-		puts(" Full access #define CONFIG_SPI_FLASH_BAR\n");
+	if (((flash->spi->is_dual == SPI_FLASH_CONN_SINGLE) &&
+			(flash->size > SPI_FLASH_16MB_BOUN)) ||
+			((flash->spi->is_dual == SPI_FLASH_CONN_DUALPARALLEL) &&
+			((flash->size / 2) > SPI_FLASH_16MB_BOUN))) {
+		puts("SF: Warning - Only lower 16MiB accessible for given");
+		puts("device, Full access #define CONFIG_SPI_FLASH_BAR\n");
 	}
 #endif
 
diff --git a/include/spi.h b/include/spi.h
index c0dab57..bb242bd 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -40,11 +40,13 @@ 
  *   cs:	ID of the chip select connected to the slave.
  *   max_write_size:	If non-zero, the maximum number of bytes which can
  *		be written at once, excluding command bytes.
+ *   is_dual:	Indicates whether dual memories are used.
  */
 struct spi_slave {
 	unsigned int	bus;
 	unsigned int	cs;
 	unsigned int max_write_size;
+	int	is_dual;
 };
 
 /*-----------------------------------------------------------------------
diff --git a/include/spi_flash.h b/include/spi_flash.h
index bfc59aa..fb132ab 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -17,6 +17,13 @@ 
 #include <linux/types.h>
 #include <linux/compiler.h>
 
+/* SPI connection modes */
+enum spi_conn_modes {
+	SPI_FLASH_CONN_UNKNOWN = -1,
+	SPI_FLASH_CONN_SINGLE,
+	SPI_FLASH_CONN_DUALPARALLEL,
+};
+
 struct spi_flash {
 	struct spi_slave *spi;