All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH 0/8] sf: improve support of (Q)SPI flash memories
@ 2017-05-19 14:59 Cyrille Pitchen
  2017-05-19 14:59 ` [U-Boot] [PATCH 1/8] spi: add support of SPI flash commands Cyrille Pitchen
                   ` (7 more replies)
  0 siblings, 8 replies; 17+ messages in thread
From: Cyrille Pitchen @ 2017-05-19 14:59 UTC (permalink / raw)
  To: u-boot

Hi all,

this series of patches has been tested on u-boot-2017.03-at91 then ported
to the 'master' branch of the u-boot-spi.git tree.

Tests were passed with a sama5d2 xplained board which embeds both SPI and
QSPI controllers.

The following tests have been passed:

- QSPI0 + Macronix MX25L25673G:
 + probe: OK
 + Fast Read 1-1-4 at offset 0x10000 (u-boot env): OK
 + Page Program 1-1-4 at offset 0x10000: OK
   The Macronix datasheet tells that only Page Program 1-4-4 is
   supported, not Page Program 1-1-4, however it worked, I don't know
   why...

- QSPI0 + Microchip SST26
  + probe: OK
  + Fast Read 1-1-4 at offset 0x10000 (u-boot env): OK
  + Page Program 1-1-1 at offset 0x10000: OK
    SST26 memories support Page Program 1-4-4 but with the op code of
    Page Program 1-1-4, which is not standard so I don't use it.

- QSPI0 + Adesto AT25DF321A
  + probe: OK
  + Fast Read 1-1-1 at offset 0x10000 (u-boot env): OK
  + Page Program 1-1-1 at offset 0x10000: OK

- SPI0 + Adesto AT25DF321A
  + probe: OK
  + Fast Read 1-1-1 at offset 0x6000 (u-boot env): OK
  + Page Program 1-1-1 at offest 0x6000: OK

- SPI1 + Atmel AT45
  + probe: OK
  + Read at offset 0: OK
  + Write at offset 0: OK <- I didn't try offset other than 0.

During my tests, I used:
- setenv/saveenv, reboot, printenv
or
- sf probe, sf read, sf write


Best regards,

Cyrille


Cyrille Pitchen (8):
  spi: add support of SPI flash commands
  sf: describe all SPI flash commands with 'struct spi_flash_command'
  sf: select the relevant SPI flash protocol for read and write commands
  sf: differentiate Page Program 1-1-4 and 1-4-4
  sf: add 'addr_len' member to 'struct spi_flash'
  sf: add new option to support SPI flash above 16MiB
  sf: add support to Microchip SST26 QSPI memories
  sf: add driver for Atmel QSPI controller

 drivers/mtd/spi/Kconfig         |  15 +-
 drivers/mtd/spi/sf.c            |  78 ++++++--
 drivers/mtd/spi/sf_dataflash.c  | 119 ++++++------
 drivers/mtd/spi/sf_internal.h   |  48 +++--
 drivers/mtd/spi/spi_flash.c     | 338 +++++++++++++++++++++++----------
 drivers/mtd/spi/spi_flash_ids.c |   5 +
 drivers/spi/Kconfig             |   7 +
 drivers/spi/Makefile            |   1 +
 drivers/spi/atmel_qspi.c        | 404 ++++++++++++++++++++++++++++++++++++++++
 drivers/spi/atmel_qspi.h        | 169 +++++++++++++++++
 drivers/spi/spi-uclass.c        |  40 ++++
 drivers/spi/spi.c               |  13 ++
 include/spi.h                   | 168 +++++++++++++++++
 include/spi_flash.h             |   6 +
 14 files changed, 1222 insertions(+), 189 deletions(-)
 create mode 100644 drivers/spi/atmel_qspi.c
 create mode 100644 drivers/spi/atmel_qspi.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 1/8] spi: add support of SPI flash commands
  2017-05-19 14:59 [U-Boot] [PATCH 0/8] sf: improve support of (Q)SPI flash memories Cyrille Pitchen
@ 2017-05-19 14:59 ` Cyrille Pitchen
  2017-05-22  1:17   ` Yang, Wenyou
  2017-05-19 14:59 ` [U-Boot] [PATCH 2/8] sf: describe all SPI flash commands with 'struct spi_flash_command' Cyrille Pitchen
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Cyrille Pitchen @ 2017-05-19 14:59 UTC (permalink / raw)
  To: u-boot

This patch introduces 'struct spi_flash_command' and functions
spi_is_flash_command_supported() / spi_exec_flash_command().

The 'struct spi_flash_command' describes all the relevant parameters to
execute any SPI flash command:
- the instruction op code
- the number of bytes used to send the address: 0, 3 or 4 bytes
- the number of mode and wait-state clock cycles, also called dummy cycles
- the number and values of data bytes to be sent or received
- the SPI x-y-z protocol [1]
- the flash command type [2]

[1] SPI x-y-z protocol:
- x is the number of I/O lines used to send the instruction op code.
- y is the number of I/O lines used during address, mode and wait-state
  clock cycles.
- z is the number of I/O lines used to send or received data.

[2] Flash command type:
The flash command type is provided to differenciate "memory"
read/write/erase operations from "flash internal register" read/write
operations. Indeed some SPI controller drivers handle those command type
in different ways.
However SPI controller drivers should not check the value of the
instruction op code to guess the actual kind of flash command to perform.
Many instruction op codes are SPI flash manufacturer specific and only
drivers/mtd/spi/spi_flash.c should have the knowledge of all of them.

Besides, more and more QSPI controllers, like those of TI and Candence,
have special way to support (Fast) Read operations using some
"memory like" area mapped into the system bus. Hence, if those drivers
choose to override the default implementation of
spi_is_flash_command_supported() so that their own functions return true
only for a "memory read" flash command type, then spi_exec_flash_command()
might be used to implement the read from the "memory like" area mapped
into the system bus.
It means that spi_exec_flash_command() could be used to supersede the
actual flash->memory_map mechanism; spi_is_flash_command_supported() /
spi_exec_flash_command() being more generic and covering more use cases.

For instance, the Atmel QSPI hardware controller uses its "memory like"
area mapped ino the system to perform not only (Fast) Read operations but
actually all other types of flash commands. Hence the regular SPI API
based on the spi_xfer() function is not suited to support the Atmel QSPI
controller.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/spi/spi-uclass.c |  40 +++++++++++
 drivers/spi/spi.c        |  13 ++++
 include/spi.h            | 168 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 221 insertions(+)

diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c
index c061c05443d4..b8092538e9b0 100644
--- a/drivers/spi/spi-uclass.c
+++ b/drivers/spi/spi-uclass.c
@@ -92,6 +92,30 @@ int dm_spi_xfer(struct udevice *dev, unsigned int bitlen,
 	return spi_get_ops(bus)->xfer(dev, bitlen, dout, din, flags);
 }
 
+bool dm_spi_is_flash_command_supported(struct udevice *dev,
+				       const struct spi_flash_command *cmd)
+{
+	struct udevice *bus = dev->parent;
+	struct dm_spi_ops *ops = spi_get_ops(bus);
+
+	if (ops->is_flash_command_supported)
+		return ops->is_flash_command_supported(dev, cmd);
+
+	return false;
+}
+
+int dm_spi_exec_flash_command(struct udevice *dev,
+			      const struct spi_flash_command *cmd)
+{
+	struct udevice *bus = dev->parent;
+	struct dm_spi_ops *ops = spi_get_ops(bus);
+
+	if (ops->exec_flash_command)
+		return ops->exec_flash_command(dev, cmd);
+
+	return -EINVAL;
+}
+
 int spi_claim_bus(struct spi_slave *slave)
 {
 	return dm_spi_claim_bus(slave->dev);
@@ -108,6 +132,18 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
 	return dm_spi_xfer(slave->dev, bitlen, dout, din, flags);
 }
 
+bool spi_is_flash_command_supported(struct spi_slave *slave,
+				    const struct spi_flash_command *cmd)
+{
+	return dm_spi_is_flash_command_supported(slave->dev, cmd);
+}
+
+int spi_exec_flash_command(struct spi_slave *slave,
+			   const struct spi_flash_command *cmd)
+{
+	return dm_spi_exec_flash_command(slave->dev, cmd);
+}
+
 #if !CONFIG_IS_ENABLED(OF_PLATDATA)
 static int spi_child_post_bind(struct udevice *dev)
 {
@@ -147,6 +183,10 @@ static int spi_post_probe(struct udevice *bus)
 		ops->set_mode += gd->reloc_off;
 	if (ops->cs_info)
 		ops->cs_info += gd->reloc_off;
+	if (ops->is_flash_command_supported)
+		ops->is_flash_command_supported += gd->reloc_off;
+	if (ops->exec_flash_command)
+		ops->exec_flash_command += gd->reloc_off;
 #endif
 
 	return 0;
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 7d81fbd7f8f5..e47acdc9e414 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -5,6 +5,7 @@
  */
 
 #include <common.h>
+#include <errno.h>
 #include <fdtdec.h>
 #include <malloc.h>
 #include <spi.h>
@@ -58,3 +59,15 @@ struct spi_slave *spi_base_setup_slave_fdt(const void *blob, int busnum,
 	return spi_setup_slave(busnum, cs, max_hz, mode);
 }
 #endif
+
+__weak bool spi_is_flash_command_supported(struct spi_slave *slave,
+					   const struct spi_flash_command *cmd)
+{
+	return false;
+}
+
+__weak int spi_exec_flash_command(struct spi_slave *slave,
+				  const struct spi_flash_command *cmd)
+{
+	return -EINVAL;
+}
diff --git a/include/spi.h b/include/spi.h
index deb65efdfb73..eec36b46897b 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -10,6 +10,8 @@
 #ifndef _SPI_H_
 #define _SPI_H_
 
+#include <linux/string.h> /* memset() */
+
 /* SPI mode flags */
 #define SPI_CPHA	BIT(0)			/* clock phase */
 #define SPI_CPOL	BIT(1)			/* clock polarity */
@@ -64,6 +66,116 @@ struct dm_spi_slave_platdata {
 #endif /* CONFIG_DM_SPI */
 
 /**
+ * enum spi_flash_protocol - SPI flash command protocol
+ */
+#define SPI_FPROTO_INST_SHIFT	16
+#define SPI_FPROTO_INST_MASK	GENMASK(23, 16)
+#define SPI_FPROTO_INST(nbits)					\
+	((((unsigned long)(nbits)) << SPI_FPROTO_INST_SHIFT) &	\
+	 SPI_FPROTO_INST_MASK)
+
+#define SPI_FPROTO_ADDR_SHIFT	8
+#define SPI_FPROTO_ADDR_MASK	GENMASK(15, 8)
+#define SPI_FPROTO_ADDR(nbits)					\
+	((((unsigned long)(nbits)) << SPI_FPROTO_ADDR_SHIFT) &	\
+	 SPI_FPROTO_ADDR_MASK)
+
+#define SPI_FPROTO_DATA_SHIFT	0
+#define SPI_FPROTO_DATA_MASK	GENMASK(7, 0)
+#define SPI_FPROTO_DATA(nbits)					\
+	((((unsigned long)(nbits)) << SPI_FPROTO_DATA_SHIFT) &	\
+	 SPI_FPROTO_DATA_MASK)
+
+#define SPI_FPROTO(inst_nbits, addr_nbits, data_nbits)	\
+	(SPI_FPROTO_INST(inst_nbits) |			\
+	 SPI_FPROTO_ADDR(addr_nbits) |			\
+	 SPI_FPROTO_DATA(data_nbits))
+
+enum spi_flash_protocol {
+	SPI_FPROTO_1_1_1 = SPI_FPROTO(1, 1, 1),
+	SPI_FPROTO_1_1_2 = SPI_FPROTO(1, 1, 2),
+	SPI_FPROTO_1_1_4 = SPI_FPROTO(1, 1, 4),
+	SPI_FPROTO_1_2_2 = SPI_FPROTO(1, 2, 2),
+	SPI_FPROTO_1_4_4 = SPI_FPROTO(1, 4, 4),
+	SPI_FPROTO_2_2_2 = SPI_FPROTO(2, 2, 2),
+	SPI_FPROTO_4_4_4 = SPI_FPROTO(4, 4, 4),
+};
+
+static inline
+u8 spi_flash_protocol_get_inst_nbits(enum spi_flash_protocol proto)
+{
+	return ((unsigned long)(proto & SPI_FPROTO_INST_MASK)) >>
+		SPI_FPROTO_INST_SHIFT;
+}
+
+static inline
+u8 spi_flash_protocol_get_addr_nbits(enum spi_flash_protocol proto)
+{
+	return ((unsigned long)(proto & SPI_FPROTO_ADDR_MASK)) >>
+		SPI_FPROTO_ADDR_SHIFT;
+}
+
+static inline
+u8 spi_flash_protocol_get_data_nbits(enum spi_flash_protocol proto)
+{
+	return ((unsigned long)(proto & SPI_FPROTO_DATA_MASK)) >>
+		SPI_FPROTO_DATA_SHIFT;
+}
+
+/**
+ * struct spi_flash_command - SPI flash command structure
+ *
+ * @instr:		Opcode sent to the SPI slave during instr clock cycles.
+ * @mode:		Value sent to the SPI slave during mode clock cycles.
+ * @num_mode_cycles:	Number of mode clock cycles.
+ * @num_wait_states:	Number of wait-state clock cycles.
+ * @addr_len:		Number of bytes sent during address clock cycles:
+ *			should be 0, 3, or 4.
+ * @addr:		Value sent to the SPI slave during address clock cycles.
+ * @data_len:		Number of bytes to be sent during data clock cycles.
+ * @tx_data:		Data sent to the SPI slave during data clock cycles.
+ * @rx_data:		Data read from the SPI slave during data clock cycles.
+ */
+struct spi_flash_command {
+	enum spi_flash_protocol proto;
+	u8 flags;
+#define SPI_FCMD_TYPE		GENMASK(2, 0)
+#define SPI_FCMD_READ		(0x0U << 0)
+#define SPI_FCMD_WRITE		(0x1U << 0)
+#define SPI_FCMD_ERASE		(0x2U << 0)
+#define SPI_FCMD_READ_REG	(0x3U << 0)
+#define SPI_FCMD_WRITE_REG	(0x4U << 0)
+
+	u8 inst;
+	u8 mode;
+	u8 num_mode_cycles;
+	u8 num_wait_states;
+	u8 addr_len;
+	u32 addr;
+	size_t data_len;
+	const void *tx_data;
+	void *rx_data;
+};
+
+/**
+ * Initialize a 'struct spi_flash_command'.
+ *
+ * @cmd:		Pointer to the 'struct spi_flash_command' to initialize.
+ * @instr:		Instruction opcode.
+ * @addr_len:		Number of address bytes.
+ */
+static inline void
+spi_flash_command_init(struct spi_flash_command *cmd,
+		       u8 inst, u8 addr_len, u8 flags)
+{
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->proto = SPI_FPROTO_1_1_1;
+	cmd->inst = inst;
+	cmd->addr_len = addr_len;
+	cmd->flags = flags;
+}
+
+/**
  * struct spi_slave - Representation of a SPI slave
  *
  * For driver model this is the per-child data used by the SPI bus. It can
@@ -252,6 +364,24 @@ int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen);
 int  spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
 		void *din, unsigned long flags);
 
+/**
+ * Check whether the given SPI flash command is supported
+ *
+ * @slave:	The SPI slave
+ * @cmd:	The SPI flash command to check.
+ */
+bool spi_is_flash_command_supported(struct spi_slave *slave,
+				    const struct spi_flash_command *cmd);
+
+/**
+ * Execute SPI flash command
+ *
+ * @slave:	The SPI slave which will execute the give SPI flash command.
+ * @cmd:	The SPI flash command to execute.
+ */
+int spi_exec_flash_command(struct spi_slave *slave,
+			   const struct spi_flash_command *cmd);
+
 /* Copy memory mapped data */
 void spi_flash_copy_mmap(void *data, void *offset, size_t len);
 
@@ -464,6 +594,26 @@ struct dm_spi_ops {
 	 *	   is invalid, other -ve value on error
 	 */
 	int (*cs_info)(struct udevice *bus, uint cs, struct spi_cs_info *info);
+
+	/**
+	 * Check whether the given SPI flash command is supported.
+	 *
+	 * @bus:	The SPI bus
+	 * @cmd:	The SPI flash command to check.
+	 * @return:	true if supported, false otherwise
+	 */
+	bool (*is_flash_command_supported)(struct udevice *bus,
+					   const struct spi_flash_command *cmd);
+
+	/**
+	 * Execute a SPI flash command
+	 *
+	 * @bus:	The SPI bus
+	 * @cmd:	The SPI flash command to execute.
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*exec_flash_command)(struct udevice *bus,
+				  const struct spi_flash_command *cmd);
 };
 
 struct dm_spi_emul_ops {
@@ -651,6 +801,24 @@ void dm_spi_release_bus(struct udevice *dev);
 int dm_spi_xfer(struct udevice *dev, unsigned int bitlen,
 		const void *dout, void *din, unsigned long flags);
 
+/**
+ * Check whether the given SPI flash command is supported.
+ *
+ * @dev:	The SPI slave device
+ * @cmd:	The SPI flash command
+ */
+bool dm_spi_is_flash_command_supported(struct udevice *dev,
+				       const struct spi_flash_command *cmd);
+
+/**
+ * Execute the given SPI flash command.
+ *
+ * @dev:	The SPI slave device
+ * @cmd:	The SPI flash command
+ */
+int dm_spi_exec_flash_command(struct udevice *dev,
+			      const struct spi_flash_command *cmd);
+
 /* Access the operations for a SPI device */
 #define spi_get_ops(dev)	((struct dm_spi_ops *)(dev)->driver->ops)
 #define spi_emul_get_ops(dev)	((struct dm_spi_emul_ops *)(dev)->driver->ops)
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 2/8] sf: describe all SPI flash commands with 'struct spi_flash_command'
  2017-05-19 14:59 [U-Boot] [PATCH 0/8] sf: improve support of (Q)SPI flash memories Cyrille Pitchen
  2017-05-19 14:59 ` [U-Boot] [PATCH 1/8] spi: add support of SPI flash commands Cyrille Pitchen
@ 2017-05-19 14:59 ` Cyrille Pitchen
  2017-05-22  1:18   ` Yang, Wenyou
  2017-05-19 14:59 ` [U-Boot] [PATCH 3/8] sf: select the relevant SPI flash protocol for read and write commands Cyrille Pitchen
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Cyrille Pitchen @ 2017-05-19 14:59 UTC (permalink / raw)
  To: u-boot

Now that the SPI sub-system API has been extended with
'struct spi_flash_command' and spi_is_flash_command_supported() /
spi_exec_flash_command() functions, we update the SPI FLASH sub-system to
use this new API.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/spi/sf.c           |  78 +++++++++++++----
 drivers/mtd/spi/sf_dataflash.c | 119 +++++++++++++-------------
 drivers/mtd/spi/sf_internal.h  |  24 +++---
 drivers/mtd/spi/spi_flash.c    | 184 +++++++++++++++++++++++------------------
 4 files changed, 236 insertions(+), 169 deletions(-)

diff --git a/drivers/mtd/spi/sf.c b/drivers/mtd/spi/sf.c
index d5e175ca0000..6178b0aa9824 100644
--- a/drivers/mtd/spi/sf.c
+++ b/drivers/mtd/spi/sf.c
@@ -9,46 +9,88 @@
 
 #include <common.h>
 #include <spi.h>
+#include <spi_flash.h>
 
-static int spi_flash_read_write(struct spi_slave *spi,
-				const u8 *cmd, size_t cmd_len,
-				const u8 *data_out, u8 *data_in,
-				size_t data_len)
+#include "sf_internal.h"
+
+static void spi_flash_addr(u32 addr, u8 addr_len, u8 *cmd_buf)
 {
+	u8 i;
+
+	for (i = 0; i < addr_len; i++)
+		cmd_buf[i] = addr >> ((addr_len - 1 - i) * 8);
+}
+
+static u8 spi_compute_num_dummy_bytes(enum spi_flash_protocol proto,
+				      u8 num_dummy_clock_cycles)
+{
+	int shift = fls(spi_flash_protocol_get_addr_nbits(proto)) - 1;
+
+	if (shift < 0)
+		shift = 0;
+	return (num_dummy_clock_cycles << shift) >> 3;
+}
+
+static int spi_flash_exec(struct spi_flash *flash,
+			  const struct spi_flash_command *cmd)
+{
+	struct spi_slave *spi = flash->spi;
+	u8 cmd_buf[SPI_FLASH_CMD_LEN];
+	size_t cmd_len, num_dummy_bytes;
 	unsigned long flags = SPI_XFER_BEGIN;
 	int ret;
 
-	if (data_len == 0)
+	if (spi_is_flash_command_supported(spi, cmd))
+		return spi_exec_flash_command(spi, cmd);
+
+	if (cmd->data_len == 0)
 		flags |= SPI_XFER_END;
 
-	ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags);
+	cmd_buf[0] = cmd->inst;
+	spi_flash_addr(cmd->addr, cmd->addr_len, cmd_buf + 1);
+	cmd_len = 1 + cmd->addr_len;
+
+	num_dummy_bytes = spi_compute_num_dummy_bytes(cmd->proto,
+						      cmd->num_mode_cycles +
+						      cmd->num_wait_states);
+	memset(cmd_buf + cmd_len, 0xff, num_dummy_bytes);
+	cmd_len += num_dummy_bytes;
+
+	ret = spi_xfer(spi, cmd_len * 8, cmd_buf, NULL, flags);
 	if (ret) {
 		debug("SF: Failed to send command (%zu bytes): %d\n",
 		      cmd_len, ret);
-	} else if (data_len != 0) {
-		ret = spi_xfer(spi, data_len * 8, data_out, data_in,
-					SPI_XFER_END);
+	} else if (cmd->data_len != 0) {
+		ret = spi_xfer(spi, cmd->data_len * 8,
+			       cmd->tx_data, cmd->rx_data,
+			       SPI_XFER_END);
 		if (ret)
 			debug("SF: Failed to transfer %zu bytes of data: %d\n",
-			      data_len, ret);
+			      cmd->data_len, ret);
 	}
 
 	return ret;
 }
 
-int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
-		size_t cmd_len, void *data, size_t data_len)
+int spi_flash_cmd_read(struct spi_flash *flash,
+		       const struct spi_flash_command *cmd)
 {
-	return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len);
+	return spi_flash_exec(flash, cmd);
 }
 
-int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len)
+int spi_flash_cmd(struct spi_flash *flash, u8 instr, void *response, size_t len)
 {
-	return spi_flash_cmd_read(spi, &cmd, 1, response, len);
+	struct spi_flash_command cmd;
+	u8 flags = (response && len) ? SPI_FCMD_READ_REG : SPI_FCMD_WRITE_REG;
+
+	spi_flash_command_init(&cmd, instr, 0, flags);
+	cmd.data_len = len;
+	cmd.rx_data = response;
+	return spi_flash_exec(flash, &cmd);
 }
 
-int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
-		const void *data, size_t data_len)
+int spi_flash_cmd_write(struct spi_flash *flash,
+			const struct spi_flash_command *cmd)
 {
-	return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len);
+	return spi_flash_exec(flash, cmd);
 }
diff --git a/drivers/mtd/spi/sf_dataflash.c b/drivers/mtd/spi/sf_dataflash.c
index bcddfa07556b..b2166ad4e5ff 100644
--- a/drivers/mtd/spi/sf_dataflash.c
+++ b/drivers/mtd/spi/sf_dataflash.c
@@ -73,7 +73,7 @@ struct dataflash {
 };
 
 /* Return the status of the DataFlash device */
-static inline int dataflash_status(struct spi_slave *spi)
+static inline int dataflash_status(struct spi_flash *spi_flash)
 {
 	int ret;
 	u8 status;
@@ -81,7 +81,7 @@ static inline int dataflash_status(struct spi_slave *spi)
 	 * NOTE:  at45db321c over 25 MHz wants to write
 	 * a dummy byte after the opcode...
 	 */
-	ret = spi_flash_cmd(spi, OP_READ_STATUS, &status, 1);
+	ret = spi_flash_cmd(spi_flash, OP_READ_STATUS, &status, 1);
 	return ret ? -EIO : status;
 }
 
@@ -90,7 +90,7 @@ static inline int dataflash_status(struct spi_slave *spi)
  * This usually takes 5-20 msec or so; more for sector erase.
  * ready: return > 0
  */
-static int dataflash_waitready(struct spi_slave *spi)
+static int dataflash_waitready(struct spi_flash *spi_flash)
 {
 	int status;
 	int timeout = 2 * CONFIG_SYS_HZ;
@@ -98,7 +98,7 @@ static int dataflash_waitready(struct spi_slave *spi)
 
 	timebase = get_timer(0);
 	do {
-		status = dataflash_status(spi);
+		status = dataflash_status(spi_flash);
 		if (status < 0)
 			status = 0;
 
@@ -114,11 +114,11 @@ static int dataflash_waitready(struct spi_slave *spi)
 /* Erase pages of flash */
 static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len)
 {
+	struct spi_flash_command	cmd;
 	struct dataflash	*dataflash;
 	struct spi_flash	*spi_flash;
 	struct spi_slave	*spi;
 	unsigned		blocksize;
-	uint8_t			*command;
 	uint32_t		rem;
 	int			status;
 
@@ -128,9 +128,6 @@ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len)
 
 	blocksize = spi_flash->page_size << 3;
 
-	memset(dataflash->command, 0 , sizeof(dataflash->command));
-	command = dataflash->command;
-
 	debug("%s: erase addr=0x%x len 0x%x\n", dev->name, offset, len);
 
 	div_u64_rem(len, spi_flash->page_size, &rem);
@@ -146,6 +143,8 @@ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len)
 		return status;
 	}
 
+	spi_flash_command_init(&cmd, OP_ERASE_BLOCK, SPI_FLASH_3B_ADDR_LEN,
+			       SPI_FCMD_ERASE);
 	while (len > 0) {
 		unsigned int	pageaddr;
 		int		do_block;
@@ -157,23 +156,24 @@ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len)
 		do_block = (pageaddr & 0x7) == 0 && len >= blocksize;
 		pageaddr = pageaddr << dataflash->page_offset;
 
-		command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
-		command[1] = (uint8_t)(pageaddr >> 16);
-		command[2] = (uint8_t)(pageaddr >> 8);
-		command[3] = 0;
+		cmd.inst = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
+		cmd.addr = pageaddr & 0x00FFFF00;
 
 		debug("%s ERASE %s: (%x) %x %x %x [%d]\n",
 		      dev->name, do_block ? "block" : "page",
-		      command[0], command[1], command[2], command[3],
+		      cmd.inst,
+		      (cmd.addr >> 16) & 0xff,
+		      (cmd.addr >>  8) & 0xff,
+		      (cmd.addr >>  0) & 0xff,
 		      pageaddr);
 
-		status = spi_flash_cmd_write(spi, command, 4, NULL, 0);
+		status = spi_flash_cmd_write(spi_flash, &cmd);
 		if (status < 0) {
 			debug("%s: erase send command error!\n", dev->name);
 			return -EIO;
 		}
 
-		status = dataflash_waitready(spi);
+		status = dataflash_waitready(spi_flash);
 		if (status < 0) {
 			debug("%s: erase waitready error!\n", dev->name);
 			return status;
@@ -202,23 +202,18 @@ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len)
 static int spi_dataflash_read(struct udevice *dev, u32 offset, size_t len,
 			      void *buf)
 {
+	struct spi_flash_command	cmd;
 	struct dataflash	*dataflash;
 	struct spi_flash	*spi_flash;
 	struct spi_slave	*spi;
 	unsigned int		addr;
-	uint8_t			*command;
 	int			status;
 
 	dataflash = dev_get_priv(dev);
 	spi_flash = dev_get_uclass_priv(dev);
 	spi = spi_flash->spi;
 
-	memset(dataflash->command, 0 , sizeof(dataflash->command));
-	command = dataflash->command;
-
-	debug("%s: erase addr=0x%x len 0x%x\n", dev->name, offset, len);
-	debug("READ: (%x) %x %x %x\n",
-	      command[0], command[1], command[2], command[3]);
+	debug("%s: read addr=0x%x len 0x%x\n", dev->name, offset, len);
 
 	/* Calculate flash page/byte address */
 	addr = (((unsigned)offset / spi_flash->page_size)
@@ -236,13 +231,15 @@ static int spi_dataflash_read(struct udevice *dev, u32 offset, size_t len,
 	 * the peak rate available.  Some chips support commands with
 	 * fewer "don't care" bytes.  Both buffers stay unchanged.
 	 */
-	command[0] = OP_READ_CONTINUOUS;
-	command[1] = (uint8_t)(addr >> 16);
-	command[2] = (uint8_t)(addr >> 8);
-	command[3] = (uint8_t)(addr >> 0);
+	spi_flash_command_init(&cmd, OP_READ_CONTINUOUS, SPI_FLASH_3B_ADDR_LEN,
+			       SPI_FCMD_READ);
+	cmd.addr = addr;
+	cmd.num_wait_states = 4 * 8; /* 4 "don't care" bytes */
+	cmd.data_len = len;
+	cmd.rx_data = buf;
 
 	/* plus 4 "don't care" bytes, command len: 4 + 4 "don't care" bytes */
-	status = spi_flash_cmd_read(spi, command, 8, buf, len);
+	status = spi_flash_cmd_read(spi_flash, &cmd);
 
 	spi_release_bus(spi);
 
@@ -258,10 +255,10 @@ static int spi_dataflash_read(struct udevice *dev, u32 offset, size_t len,
 int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
 			const void *buf)
 {
+	struct spi_flash_command	cmd;
 	struct dataflash	*dataflash;
 	struct spi_flash	*spi_flash;
 	struct spi_slave	*spi;
-	uint8_t			*command;
 	unsigned int		pageaddr, addr, to, writelen;
 	size_t			remaining = len;
 	u_char			*writebuf = (u_char *)buf;
@@ -271,9 +268,6 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
 	spi_flash = dev_get_uclass_priv(dev);
 	spi = spi_flash->spi;
 
-	memset(dataflash->command, 0 , sizeof(dataflash->command));
-	command = dataflash->command;
-
 	debug("%s: write 0x%x..0x%x\n", dev->name, offset, (offset + len));
 
 	pageaddr = ((unsigned)offset / spi_flash->page_size);
@@ -289,6 +283,8 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
 		return status;
 	}
 
+	spi_flash_command_init(&cmd, OP_TRANSFER_BUF1, SPI_FLASH_3B_ADDR_LEN,
+			       SPI_FCMD_WRITE);
 	while (remaining > 0) {
 		debug("write @ %d:%d len=%d\n", pageaddr, to, writelen);
 
@@ -313,22 +309,25 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
 
 		/* (1) Maybe transfer partial page to Buffer1 */
 		if (writelen != spi_flash->page_size) {
-			command[0] = OP_TRANSFER_BUF1;
-			command[1] = (addr & 0x00FF0000) >> 16;
-			command[2] = (addr & 0x0000FF00) >> 8;
-			command[3] = 0;
+			cmd.inst = OP_TRANSFER_BUF1;
+			cmd.addr = (addr & 0x00FFFF00);
+			cmd.data_len = 0;
+			cmd.tx_data = NULL;
 
 			debug("TRANSFER: (%x) %x %x %x\n",
-			      command[0], command[1], command[2], command[3]);
+			      cmd.inst,
+			      (cmd.addr >> 16) & 0xff,
+			      (cmd.addr >>  8) & 0xff,
+			      (cmd.addr >>  0) & 0xff);
 
-			status = spi_flash_cmd_write(spi, command, 4, NULL, 0);
+			status = spi_flash_cmd_write(spi_flash, &cmd);
 			if (status < 0) {
 				debug("%s: write(<pagesize) command error!\n",
 				      dev->name);
 				return -EIO;
 			}
 
-			status = dataflash_waitready(spi);
+			status = dataflash_waitready(spi_flash);
 			if (status < 0) {
 				debug("%s: write(<pagesize) waitready error!\n",
 				      dev->name);
@@ -338,22 +337,24 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
 
 		/* (2) Program full page via Buffer1 */
 		addr += to;
-		command[0] = OP_PROGRAM_VIA_BUF1;
-		command[1] = (addr & 0x00FF0000) >> 16;
-		command[2] = (addr & 0x0000FF00) >> 8;
-		command[3] = (addr & 0x000000FF);
+		cmd.inst = OP_PROGRAM_VIA_BUF1;
+		cmd.addr = addr;
+		cmd.data_len = writelen;
+		cmd.tx_data = writebuf;
 
 		debug("PROGRAM: (%x) %x %x %x\n",
-		      command[0], command[1], command[2], command[3]);
+		      cmd.inst,
+		      (cmd.addr >> 16) & 0xff,
+		      (cmd.addr >>  8) & 0xff,
+		      (cmd.addr >>  0) & 0xff);
 
-		status = spi_flash_cmd_write(spi, command,
-					     4, writebuf, writelen);
+		status = spi_flash_cmd_write(spi_flash, &cmd);
 		if (status < 0) {
 			debug("%s: write send command error!\n", dev->name);
 			return -EIO;
 		}
 
-		status = dataflash_waitready(spi);
+		status = dataflash_waitready(spi_flash);
 		if (status < 0) {
 			debug("%s: write waitready error!\n", dev->name);
 			return status;
@@ -362,16 +363,18 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
 #ifdef CONFIG_SPI_DATAFLASH_WRITE_VERIFY
 		/* (3) Compare to Buffer1 */
 		addr = pageaddr << dataflash->page_offset;
-		command[0] = OP_COMPARE_BUF1;
-		command[1] = (addr & 0x00FF0000) >> 16;
-		command[2] = (addr & 0x0000FF00) >> 8;
-		command[3] = 0;
+		cmd.inst = OP_COMPARE_BUF1;
+		cmd.addr = addr & 0x00FFFF00;
+		cmd.data_len = writelen;
+		cmd.tx_data = writebuf;
 
 		debug("COMPARE: (%x) %x %x %x\n",
-		      command[0], command[1], command[2], command[3]);
+		      cmd.inst,
+		      (cmd.addr >> 16) & 0xff,
+		      (cmd.addr >>  8) & 0xff,
+		      (cmd.addr >>  0) & 0xff);
 
-		status = spi_flash_cmd_write(spi, command,
-					     4, writebuf, writelen);
+		status = spi_flash_cmd_write(spi, &cmd);
 		if (status < 0) {
 			debug("%s: write(compare) send command error!\n",
 			      dev->name);
@@ -496,7 +499,7 @@ static struct flash_info dataflash_data[] = {
 	{ "at45db642d",  0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS},
 };
 
-static struct flash_info *jedec_probe(struct spi_slave *spi)
+static struct flash_info *jedec_probe(struct spi_flash *spi_flash)
 {
 	int			tmp;
 	uint8_t			id[5];
@@ -513,7 +516,7 @@ static struct flash_info *jedec_probe(struct spi_slave *spi)
 	 * That's not an error; only rev C and newer chips handle it, and
 	 * only Atmel sells these chips.
 	 */
-	tmp = spi_flash_cmd(spi, CMD_READ_ID, id, sizeof(id));
+	tmp = spi_flash_cmd(spi_flash, CMD_READ_ID, id, sizeof(id));
 	if (tmp < 0) {
 		printf("dataflash: error %d reading JEDEC ID\n", tmp);
 		return ERR_PTR(tmp);
@@ -532,7 +535,7 @@ static struct flash_info *jedec_probe(struct spi_slave *spi)
 			tmp++, info++) {
 		if (info->jedec_id == jedec) {
 			if (info->flags & SUP_POW2PS) {
-				status = dataflash_status(spi);
+				status = dataflash_status(spi_flash);
 				if (status < 0) {
 					debug("dataflash: status error %d\n",
 					      status);
@@ -596,7 +599,7 @@ static int spi_dataflash_probe(struct udevice *dev)
 	 * Both support the security register, though with different
 	 * write procedures.
 	 */
-	info = jedec_probe(spi);
+	info = jedec_probe(spi_flash);
 	if (IS_ERR(info))
 		goto err_jedec_probe;
 	if (info != NULL) {
@@ -611,7 +614,7 @@ static int spi_dataflash_probe(struct udevice *dev)
 	* Older chips support only legacy commands, identifing
 	* capacity using bits in the status byte.
 	*/
-	status = dataflash_status(spi);
+	status = dataflash_status(spi_flash);
 	if (status <= 0 || status == 0xff) {
 		printf("dataflash: read status error %d\n", status);
 		if (status == 0 || status == 0xff)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index 839cdbe1b0f1..5c551089d673 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -27,7 +27,7 @@ enum spi_nor_option_flags {
 };
 
 #define SPI_FLASH_3B_ADDR_LEN		3
-#define SPI_FLASH_CMD_LEN		(1 + SPI_FLASH_3B_ADDR_LEN)
+#define SPI_FLASH_CMD_LEN		(1 + SPI_FLASH_3B_ADDR_LEN + 16)
 #define SPI_FLASH_16MB_BOUN		0x1000000
 
 /* CFI Manufacture ID's */
@@ -137,21 +137,21 @@ struct spi_flash_info {
 extern const struct spi_flash_info spi_flash_ids[];
 
 /* Send a single-byte command to the device and read the response */
-int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len);
+int spi_flash_cmd(struct spi_flash *flash, u8 instr, void *response, size_t len);
 
 /*
  * Send a multi-byte command to the device and read the response. Used
  * for flash array reads, etc.
  */
-int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
-		size_t cmd_len, void *data, size_t data_len);
+int spi_flash_cmd_read(struct spi_flash *flash,
+		       const struct spi_flash_command *cmd);
 
 /*
  * Send a multi-byte command to the device followed by (optional)
  * data. Used for programming the flash array, etc.
  */
-int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
-		const void *data, size_t data_len);
+int spi_flash_cmd_write(struct spi_flash *flash,
+			const struct spi_flash_command *cmd);
 
 
 /* Flash erase(sectors) operation, support all possible erase commands */
@@ -169,13 +169,13 @@ int stm_is_locked(struct spi_flash *flash, u32 ofs, size_t len);
 /* Enable writing on the SPI flash */
 static inline int spi_flash_cmd_write_enable(struct spi_flash *flash)
 {
-	return spi_flash_cmd(flash->spi, CMD_WRITE_ENABLE, NULL, 0);
+	return spi_flash_cmd(flash, CMD_WRITE_ENABLE, NULL, 0);
 }
 
 /* Disable writing on the SPI flash */
 static inline int spi_flash_cmd_write_disable(struct spi_flash *flash)
 {
-	return spi_flash_cmd(flash->spi, CMD_WRITE_DISABLE, NULL, 0);
+	return spi_flash_cmd(flash, CMD_WRITE_DISABLE, NULL, 0);
 }
 
 /*
@@ -186,8 +186,8 @@ static inline int spi_flash_cmd_write_disable(struct spi_flash *flash)
  * - spi_flash_wait_till_ready
  * - SPI release
  */
-int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
-		size_t cmd_len, const void *buf, size_t buf_len);
+int spi_flash_write_common(struct spi_flash *flash,
+			   const struct spi_flash_command *cmd);
 
 /*
  * Flash write operation, support all possible write commands.
@@ -201,8 +201,8 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
  * Same as spi_flash_cmd_read() except it also claims/releases the SPI
  * bus. Used as common part of the ->read() operation.
  */
-int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
-		size_t cmd_len, void *data, size_t data_len);
+int spi_flash_read_common(struct spi_flash *flash,
+			  const struct spi_flash_command *cmd);
 
 /* Flash read operation, support all possible read commands */
 int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index ab7910bc1450..d0634e5b28c7 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -22,21 +22,15 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-static void spi_flash_addr(u32 addr, u8 *cmd)
-{
-	/* cmd[0] is actual command */
-	cmd[1] = addr >> 16;
-	cmd[2] = addr >> 8;
-	cmd[3] = addr >> 0;
-}
-
 static int read_sr(struct spi_flash *flash, u8 *rs)
 {
+	struct spi_flash_command cmd;
 	int ret;
-	u8 cmd;
 
-	cmd = CMD_READ_STATUS;
-	ret = spi_flash_read_common(flash, &cmd, 1, rs, 1);
+	spi_flash_command_init(&cmd, CMD_READ_STATUS, 0, SPI_FCMD_READ_REG);
+	cmd.data_len = 1;
+	cmd.rx_data = rs;
+	ret = spi_flash_read_common(flash, &cmd);
 	if (ret < 0) {
 		debug("SF: fail to read status register\n");
 		return ret;
@@ -47,10 +41,13 @@ static int read_sr(struct spi_flash *flash, u8 *rs)
 
 static int read_fsr(struct spi_flash *flash, u8 *fsr)
 {
+	struct spi_flash_command cmd;
 	int ret;
-	const u8 cmd = CMD_FLAG_STATUS;
 
-	ret = spi_flash_read_common(flash, &cmd, 1, fsr, 1);
+	spi_flash_command_init(&cmd, CMD_FLAG_STATUS, 0, SPI_FCMD_READ_REG);
+	cmd.data_len = 1;
+	cmd.rx_data = fsr;
+	ret = spi_flash_read_common(flash, &cmd);
 	if (ret < 0) {
 		debug("SF: fail to read flag status register\n");
 		return ret;
@@ -61,11 +58,13 @@ static int read_fsr(struct spi_flash *flash, u8 *fsr)
 
 static int write_sr(struct spi_flash *flash, u8 ws)
 {
-	u8 cmd;
+	struct spi_flash_command cmd;
 	int ret;
 
-	cmd = CMD_WRITE_STATUS;
-	ret = spi_flash_write_common(flash, &cmd, 1, &ws, 1);
+	spi_flash_command_init(&cmd, CMD_WRITE_STATUS, 0, SPI_FCMD_WRITE_REG);
+	cmd.data_len = 1;
+	cmd.tx_data = &ws;
+	ret = spi_flash_write_common(flash, &cmd);
 	if (ret < 0) {
 		debug("SF: fail to write status register\n");
 		return ret;
@@ -77,11 +76,13 @@ static int write_sr(struct spi_flash *flash, u8 ws)
 #if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
 static int read_cr(struct spi_flash *flash, u8 *rc)
 {
+	struct spi_flash_command cmd;
 	int ret;
-	u8 cmd;
 
-	cmd = CMD_READ_CONFIG;
-	ret = spi_flash_read_common(flash, &cmd, 1, rc, 1);
+	spi_flash_command_init(&cmd, CMD_READ_CONFIG, 0, SPI_FCMD_READ_REG);
+	cmd.data_len = 1;
+	cmd.rx_data = rc;
+	ret = spi_flash_read_common(flash, &cmd);
 	if (ret < 0) {
 		debug("SF: fail to read config register\n");
 		return ret;
@@ -92,17 +93,19 @@ static int read_cr(struct spi_flash *flash, u8 *rc)
 
 static int write_cr(struct spi_flash *flash, u8 wc)
 {
+	struct spi_flash_command cmd;
 	u8 data[2];
-	u8 cmd;
 	int ret;
 
 	ret = read_sr(flash, &data[0]);
 	if (ret < 0)
 		return ret;
 
-	cmd = CMD_WRITE_STATUS;
+	spi_flash_command_init(&cmd, CMD_WRITE_STATUS, 0, SPI_FCMD_WRITE_REG);
+	cmd.data_len = 2;
+	cmd.tx_data = data;
 	data[1] = wc;
-	ret = spi_flash_write_common(flash, &cmd, 1, &data, 2);
+	ret = spi_flash_write_common(flash, &cmd);
 	if (ret) {
 		debug("SF: fail to write config register\n");
 		return ret;
@@ -115,15 +118,19 @@ static int write_cr(struct spi_flash *flash, u8 wc)
 #ifdef CONFIG_SPI_FLASH_BAR
 static int write_bar(struct spi_flash *flash, u32 offset)
 {
-	u8 cmd, bank_sel;
+	struct spi_flash_command cmd;
+	u8 bank_sel;
 	int ret;
 
 	bank_sel = offset / (SPI_FLASH_16MB_BOUN << flash->shift);
 	if (bank_sel == flash->bank_curr)
 		goto bar_end;
 
-	cmd = flash->bank_write_cmd;
-	ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1);
+	spi_flash_command_init(&cmd, flash->bank_write_cmd, 0,
+			       SPI_FCMD_WRITE_REG);
+	cmd.data_len = 1;
+	cmd.tx_data = &bank_sel;
+	ret = spi_flash_write_common(flash, &cmd);
 	if (ret < 0) {
 		debug("SF: fail to write bank register\n");
 		return ret;
@@ -136,6 +143,7 @@ bar_end:
 
 static int read_bar(struct spi_flash *flash, const struct spi_flash_info *info)
 {
+	struct spi_flash_command cmd;
 	u8 curr_bank = 0;
 	int ret;
 
@@ -152,8 +160,11 @@ static int read_bar(struct spi_flash *flash, const struct spi_flash_info *info)
 		flash->bank_write_cmd = CMD_EXTNADDR_WREAR;
 	}
 
-	ret = spi_flash_read_common(flash, &flash->bank_read_cmd, 1,
-				    &curr_bank, 1);
+	spi_flash_command_init(&cmd, flash->bank_read_cmd, 0,
+			       SPI_FCMD_READ_REG);
+	cmd.data_len = 1;
+	cmd.rx_data = &curr_bank;
+	ret = spi_flash_read_common(flash, &cmd);
 	if (ret) {
 		debug("SF: fail to read bank addr register\n");
 		return ret;
@@ -250,14 +261,14 @@ static int spi_flash_wait_till_ready(struct spi_flash *flash,
 	return -ETIMEDOUT;
 }
 
-int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
-		size_t cmd_len, const void *buf, size_t buf_len)
+int spi_flash_write_common(struct spi_flash *flash,
+			   const struct spi_flash_command *cmd)
 {
 	struct spi_slave *spi = flash->spi;
 	unsigned long timeout = SPI_FLASH_PROG_TIMEOUT;
 	int ret;
 
-	if (buf == NULL)
+	if (cmd->tx_data == NULL)
 		timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT;
 
 	ret = spi_claim_bus(spi);
@@ -272,7 +283,7 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
 		return ret;
 	}
 
-	ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len);
+	ret = spi_flash_cmd_write(flash, cmd);
 	if (ret < 0) {
 		debug("SF: write cmd failed\n");
 		return ret;
@@ -293,8 +304,8 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
 
 int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
 {
+	struct spi_flash_command cmd;
 	u32 erase_size, erase_addr;
-	u8 cmd[SPI_FLASH_CMD_LEN];
 	int ret = -1;
 
 	erase_size = flash->erase_size;
@@ -311,7 +322,8 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
 		}
 	}
 
-	cmd[0] = flash->erase_cmd;
+	spi_flash_command_init(&cmd, flash->erase_cmd, SPI_FLASH_3B_ADDR_LEN,
+			       SPI_FCMD_ERASE);
 	while (len) {
 		erase_addr = offset;
 
@@ -324,12 +336,15 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
 		if (ret < 0)
 			return ret;
 #endif
-		spi_flash_addr(erase_addr, cmd);
+		cmd.addr = erase_addr;
 
-		debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1],
-		      cmd[2], cmd[3], erase_addr);
+		debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd.inst,
+		      (cmd.addr >> 16) & 0xff,
+		      (cmd.addr >>  8) & 0xff,
+		      (cmd.addr >>  0) & 0xff,
+		      erase_addr);
 
-		ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0);
+		ret = spi_flash_write_common(flash, &cmd);
 		if (ret < 0) {
 			debug("SF: erase failed\n");
 			break;
@@ -346,10 +361,10 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
 		size_t len, const void *buf)
 {
 	struct spi_slave *spi = flash->spi;
+	struct spi_flash_command cmd;
 	unsigned long byte_addr, page_size;
 	u32 write_addr;
 	size_t chunk_len, actual;
-	u8 cmd[SPI_FLASH_CMD_LEN];
 	int ret = -1;
 
 	page_size = flash->page_size;
@@ -362,7 +377,8 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
 		}
 	}
 
-	cmd[0] = flash->write_cmd;
+	spi_flash_command_init(&cmd, flash->write_cmd, SPI_FLASH_3B_ADDR_LEN,
+			       SPI_FCMD_WRITE);
 	for (actual = 0; actual < len; actual += chunk_len) {
 		write_addr = offset;
 
@@ -382,13 +398,18 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
 			chunk_len = min(chunk_len,
 					(size_t)spi->max_write_size);
 
-		spi_flash_addr(write_addr, cmd);
+		cmd.addr = write_addr;
+		cmd.data_len = chunk_len;
+		cmd.tx_data = buf + actual;
 
 		debug("SF: 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);
+		      buf + actual, cmd.inst,
+		      (cmd.addr >> 16) & 0xff,
+		      (cmd.addr >>  8) & 0xff,
+		      (cmd.addr >>  0) & 0xff,
+		      chunk_len);
 
-		ret = spi_flash_write_common(flash, cmd, sizeof(cmd),
-					buf + actual, chunk_len);
+		ret = spi_flash_write_common(flash, &cmd);
 		if (ret < 0) {
 			debug("SF: write failed\n");
 			break;
@@ -400,8 +421,8 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
 	return ret;
 }
 
-int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
-		size_t cmd_len, void *data, size_t data_len)
+int spi_flash_read_common(struct spi_flash *flash,
+			  const struct spi_flash_command *cmd)
 {
 	struct spi_slave *spi = flash->spi;
 	int ret;
@@ -412,7 +433,7 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
 		return ret;
 	}
 
-	ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len);
+	ret = spi_flash_cmd_read(flash, cmd);
 	if (ret < 0) {
 		debug("SF: read cmd failed\n");
 		return ret;
@@ -440,7 +461,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
 		size_t len, void *data)
 {
 	struct spi_slave *spi = flash->spi;
-	u8 *cmd, cmdsz;
+	struct spi_flash_command cmd;
 	u32 remain_len, read_len, read_addr;
 	int bank_sel = 0;
 	int ret = -1;
@@ -459,14 +480,9 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
 		return 0;
 	}
 
-	cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte;
-	cmd = calloc(1, cmdsz);
-	if (!cmd) {
-		debug("SF: Failed to allocate cmd\n");
-		return -ENOMEM;
-	}
-
-	cmd[0] = flash->read_cmd;
+	spi_flash_command_init(&cmd, flash->read_cmd, SPI_FLASH_3B_ADDR_LEN,
+			       SPI_FCMD_READ);
+	cmd.num_wait_states = flash->dummy_byte * 8;
 	while (len) {
 		read_addr = offset;
 
@@ -487,9 +503,10 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
 		else
 			read_len = remain_len;
 
-		spi_flash_addr(read_addr, cmd);
-
-		ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len);
+		cmd.addr = read_addr;
+		cmd.data_len = read_len;
+		cmd.rx_data = data;
+		ret = spi_flash_read_common(flash, &cmd);
 		if (ret < 0) {
 			debug("SF: read failed\n");
 			break;
@@ -500,30 +517,33 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
 		data += read_len;
 	}
 
-	free(cmd);
 	return ret;
 }
 
 #ifdef CONFIG_SPI_FLASH_SST
 static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf)
 {
-	struct spi_slave *spi = flash->spi;
+	struct spi_flash_command cmd;
 	int ret;
-	u8 cmd[4] = {
-		CMD_SST_BP,
-		offset >> 16,
-		offset >> 8,
-		offset,
-	};
+	u8 sr = 0xFFu;
+
+	spi_flash_command_init(&cmd, CMD_SST_BP, SPI_FLASH_3B_ADDR_LEN,
+			       SPI_FCMD_WRITE);
+	cmd.addr = offset;
+	cmd.data_len = 1;
+	cmd.tx_data = buf;
 
+#ifdef DEBUG
+	read_sr(flash, &sr);
+#endif
 	debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
-	      spi_w8r8(spi, CMD_READ_STATUS), buf, cmd[0], offset);
+	      sr, buf, cmd.inst, offset);
 
 	ret = spi_flash_cmd_write_enable(flash);
 	if (ret)
 		return ret;
 
-	ret = spi_flash_cmd_write(spi, cmd, sizeof(cmd), buf, 1);
+	ret = spi_flash_cmd_write(flash, &cmd);
 	if (ret)
 		return ret;
 
@@ -534,9 +554,10 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
 		const void *buf)
 {
 	struct spi_slave *spi = flash->spi;
-	size_t actual, cmd_len;
+	struct spi_flash_command cmd;
+	size_t actual;
 	int ret;
-	u8 cmd[4];
+	u8 sr = 0xFFu;
 
 	ret = spi_claim_bus(spi);
 	if (ret) {
@@ -557,19 +578,20 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
 	if (ret)
 		goto done;
 
-	cmd_len = 4;
-	cmd[0] = CMD_SST_AAI_WP;
-	cmd[1] = offset >> 16;
-	cmd[2] = offset >> 8;
-	cmd[3] = offset;
+	spi_flash_command_init(&cmd, CMD_SST_AAI_WP, SPI_FLASH_3B_ADDR_LEN,
+			       SPI_FCMD_WRITE);
+	cmd.addr = offset;
+	cmd.data_len = 2;
 
 	for (; actual < len - 1; actual += 2) {
+#ifdef DEBUG
+		read_sr(flash, &sr);
+#endif
 		debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
-		      spi_w8r8(spi, CMD_READ_STATUS), buf + actual,
-		      cmd[0], offset);
+		      sr, buf + actual, cmd.inst, offset);
 
-		ret = spi_flash_cmd_write(spi, cmd, cmd_len,
-					buf + actual, 2);
+		cmd.tx_data = buf + actual;
+		ret = spi_flash_cmd_write(flash, &cmd);
 		if (ret) {
 			debug("SF: sst word program failed\n");
 			break;
@@ -579,7 +601,7 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
 		if (ret)
 			break;
 
-		cmd_len = 1;
+		cmd.addr_len = 0;
 		offset += 2;
 	}
 
@@ -869,7 +891,7 @@ static const struct spi_flash_info *spi_flash_read_id(struct spi_flash *flash)
 	u8				id[SPI_FLASH_MAX_ID_LEN];
 	const struct spi_flash_info	*info;
 
-	tmp = spi_flash_cmd(flash->spi, CMD_READ_ID, id, SPI_FLASH_MAX_ID_LEN);
+	tmp = spi_flash_cmd(flash, CMD_READ_ID, id, SPI_FLASH_MAX_ID_LEN);
 	if (tmp < 0) {
 		printf("SF: error %d reading JEDEC ID\n", tmp);
 		return ERR_PTR(tmp);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 3/8] sf: select the relevant SPI flash protocol for read and write commands
  2017-05-19 14:59 [U-Boot] [PATCH 0/8] sf: improve support of (Q)SPI flash memories Cyrille Pitchen
  2017-05-19 14:59 ` [U-Boot] [PATCH 1/8] spi: add support of SPI flash commands Cyrille Pitchen
  2017-05-19 14:59 ` [U-Boot] [PATCH 2/8] sf: describe all SPI flash commands with 'struct spi_flash_command' Cyrille Pitchen
@ 2017-05-19 14:59 ` Cyrille Pitchen
  2017-05-22  1:18   ` Yang, Wenyou
  2017-05-19 14:59 ` [U-Boot] [PATCH 4/8] sf: differentiate Page Program 1-1-4 and 1-4-4 Cyrille Pitchen
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Cyrille Pitchen @ 2017-05-19 14:59 UTC (permalink / raw)
  To: u-boot

SPI controller drivers should not check the instruction op code byte to
guess which SPI x-y-z protocol is to be used for Fast Read or Page Program
operations.

Indeed, the op code values are not so reliable. For instance, the 32h op
code is generally used for Page Program 1-1-4 operations. However
Microchip SST26 memories use this 32h op code for their Page Program 1-4-4
operations. There are many other examples of those SPI flash manufacturer
quirks.

Instead, the SPI FLASH sub-system now fills the 'proto' member
of 'struct spi_flash_command' with flash->read_proto for Fast Read
operations and flash->write_proto for Page Program operations.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/spi/spi_flash.c | 24 ++++++++++++++++--------
 include/spi_flash.h         |  4 ++++
 2 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index d0634e5b28c7..7e35fb9f4802 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -379,6 +379,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
 
 	spi_flash_command_init(&cmd, flash->write_cmd, SPI_FLASH_3B_ADDR_LEN,
 			       SPI_FCMD_WRITE);
+	cmd.proto = flash->write_proto;
 	for (actual = 0; actual < len; actual += chunk_len) {
 		write_addr = offset;
 
@@ -482,6 +483,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
 
 	spi_flash_command_init(&cmd, flash->read_cmd, SPI_FLASH_3B_ADDR_LEN,
 			       SPI_FCMD_READ);
+	cmd.proto = flash->read_proto;
 	cmd.num_wait_states = flash->dummy_byte * 8;
 	while (len) {
 		read_addr = offset;
@@ -1045,24 +1047,30 @@ int spi_flash_scan(struct spi_flash *flash)
 
 	/* Look for read commands */
 	flash->read_cmd = CMD_READ_ARRAY_FAST;
-	if (spi->mode & SPI_RX_SLOW)
+	flash->read_proto = SPI_FPROTO_1_1_1;
+	if (spi->mode & SPI_RX_SLOW) {
 		flash->read_cmd = CMD_READ_ARRAY_SLOW;
-	else if (spi->mode & SPI_RX_QUAD && info->flags & RD_QUAD)
+	} else if (spi->mode & SPI_RX_QUAD && info->flags & RD_QUAD) {
 		flash->read_cmd = CMD_READ_QUAD_OUTPUT_FAST;
-	else if (spi->mode & SPI_RX_DUAL && info->flags & RD_DUAL)
+		flash->read_proto = SPI_FPROTO_1_1_4;
+	} else if (spi->mode & SPI_RX_DUAL && info->flags & RD_DUAL) {
 		flash->read_cmd = CMD_READ_DUAL_OUTPUT_FAST;
+		flash->read_proto = SPI_FPROTO_1_1_2;
+	}
 
 	/* Look for write commands */
-	if (info->flags & WR_QPP && spi->mode & SPI_TX_QUAD)
+	if (info->flags & WR_QPP && spi->mode & SPI_TX_QUAD) {
 		flash->write_cmd = CMD_QUAD_PAGE_PROGRAM;
-	else
+		flash->write_proto = SPI_FPROTO_1_1_4;
+	} else {
 		/* Go for default supported write cmd */
 		flash->write_cmd = CMD_PAGE_PROGRAM;
+		flash->write_proto = SPI_FPROTO_1_1_1;
+	}
 
 	/* Set the quad enable bit - only for quad commands */
-	if ((flash->read_cmd == CMD_READ_QUAD_OUTPUT_FAST) ||
-	    (flash->read_cmd == CMD_READ_QUAD_IO_FAST) ||
-	    (flash->write_cmd == CMD_QUAD_PAGE_PROGRAM)) {
+	if (spi_flash_protocol_get_data_nbits(flash->read_proto) == 4 ||
+	    spi_flash_protocol_get_data_nbits(flash->write_proto) == 4) {
 		ret = set_quad_mode(flash, info);
 		if (ret) {
 			debug("SF: Fail to set QEB for %02x\n",
diff --git a/include/spi_flash.h b/include/spi_flash.h
index be2fe3f84cb9..ac2b37f0202f 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -48,6 +48,8 @@ struct spi_slave;
  * @read_cmd:		Read cmd - Array Fast, Extn read and quad read.
  * @write_cmd:		Write cmd - page and quad program.
  * @dummy_byte:		Dummy cycles for read operation.
+ * @read_proto:		SPI x-y-z protocol for flash read ops
+ * @write_proto:	SPI x-y-z protocol for flash write ops
  * @memory_map:		Address of read-only SPI flash access
  * @flash_lock:		lock a region of the SPI Flash
  * @flash_unlock:	unlock a region of the SPI Flash
@@ -83,6 +85,8 @@ struct spi_flash {
 	u8 read_cmd;
 	u8 write_cmd;
 	u8 dummy_byte;
+	enum spi_flash_protocol read_proto;
+	enum spi_flash_protocol write_proto;
 
 	void *memory_map;
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 4/8] sf: differentiate Page Program 1-1-4 and 1-4-4
  2017-05-19 14:59 [U-Boot] [PATCH 0/8] sf: improve support of (Q)SPI flash memories Cyrille Pitchen
                   ` (2 preceding siblings ...)
  2017-05-19 14:59 ` [U-Boot] [PATCH 3/8] sf: select the relevant SPI flash protocol for read and write commands Cyrille Pitchen
@ 2017-05-19 14:59 ` Cyrille Pitchen
  2017-05-22  1:19   ` Yang, Wenyou
  2017-05-19 14:59 ` [U-Boot] [PATCH 5/8] sf: add 'addr_len' member to 'struct spi_flash' Cyrille Pitchen
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Cyrille Pitchen @ 2017-05-19 14:59 UTC (permalink / raw)
  To: u-boot

This patch simply renames the ambiguous CMD_QUAD_PAGE_PROGRAM macro
into the more explicit CMD_PAGE_PROGRAM_1_1_4.
Also it defines the CMD_PAGE_PROGRAM_1_4_4 macro to the standard 38h op
code.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/spi/sf_internal.h | 3 ++-
 drivers/mtd/spi/spi_flash.c   | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index 5c551089d673..8b8c951bcc55 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -48,7 +48,8 @@ enum spi_nor_option_flags {
 #define CMD_PAGE_PROGRAM		0x02
 #define CMD_WRITE_DISABLE		0x04
 #define CMD_WRITE_ENABLE		0x06
-#define CMD_QUAD_PAGE_PROGRAM		0x32
+#define CMD_PAGE_PROGRAM_1_1_4		0x32
+#define CMD_PAGE_PROGRAM_1_4_4		0x38
 
 /* Read commands */
 #define CMD_READ_ARRAY_SLOW		0x03
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index 7e35fb9f4802..ec998166017d 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -1060,7 +1060,7 @@ int spi_flash_scan(struct spi_flash *flash)
 
 	/* Look for write commands */
 	if (info->flags & WR_QPP && spi->mode & SPI_TX_QUAD) {
-		flash->write_cmd = CMD_QUAD_PAGE_PROGRAM;
+		flash->write_cmd = CMD_PAGE_PROGRAM_1_1_4;
 		flash->write_proto = SPI_FPROTO_1_1_4;
 	} else {
 		/* Go for default supported write cmd */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 5/8] sf: add 'addr_len' member to 'struct spi_flash'
  2017-05-19 14:59 [U-Boot] [PATCH 0/8] sf: improve support of (Q)SPI flash memories Cyrille Pitchen
                   ` (3 preceding siblings ...)
  2017-05-19 14:59 ` [U-Boot] [PATCH 4/8] sf: differentiate Page Program 1-1-4 and 1-4-4 Cyrille Pitchen
@ 2017-05-19 14:59 ` Cyrille Pitchen
  2017-05-22  1:19   ` Yang, Wenyou
  2017-05-19 14:59 ` [U-Boot] [PATCH 6/8] sf: add new option to support SPI flash above 16MiB Cyrille Pitchen
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 17+ messages in thread
From: Cyrille Pitchen @ 2017-05-19 14:59 UTC (permalink / raw)
  To: u-boot

This is a transitional patch to prepare the SPI FLASH sub-system to
support the 4-byte address instruction set later.
For now, flash->addr_len is always set to SPI_FLASH_3B_ADDR_LEN.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/spi/spi_flash.c | 13 ++++++++-----
 include/spi_flash.h         |  2 ++
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index ec998166017d..c5e00772f241 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -322,7 +322,7 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
 		}
 	}
 
-	spi_flash_command_init(&cmd, flash->erase_cmd, SPI_FLASH_3B_ADDR_LEN,
+	spi_flash_command_init(&cmd, flash->erase_cmd, flash->addr_len,
 			       SPI_FCMD_ERASE);
 	while (len) {
 		erase_addr = offset;
@@ -377,7 +377,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
 		}
 	}
 
-	spi_flash_command_init(&cmd, flash->write_cmd, SPI_FLASH_3B_ADDR_LEN,
+	spi_flash_command_init(&cmd, flash->write_cmd, flash->addr_len,
 			       SPI_FCMD_WRITE);
 	cmd.proto = flash->write_proto;
 	for (actual = 0; actual < len; actual += chunk_len) {
@@ -481,7 +481,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
 		return 0;
 	}
 
-	spi_flash_command_init(&cmd, flash->read_cmd, SPI_FLASH_3B_ADDR_LEN,
+	spi_flash_command_init(&cmd, flash->read_cmd, flash->addr_len,
 			       SPI_FCMD_READ);
 	cmd.proto = flash->read_proto;
 	cmd.num_wait_states = flash->dummy_byte * 8;
@@ -529,7 +529,7 @@ static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf)
 	int ret;
 	u8 sr = 0xFFu;
 
-	spi_flash_command_init(&cmd, CMD_SST_BP, SPI_FLASH_3B_ADDR_LEN,
+	spi_flash_command_init(&cmd, CMD_SST_BP, flash->addr_len,
 			       SPI_FCMD_WRITE);
 	cmd.addr = offset;
 	cmd.data_len = 1;
@@ -580,7 +580,7 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
 	if (ret)
 		goto done;
 
-	spi_flash_command_init(&cmd, CMD_SST_AAI_WP, SPI_FLASH_3B_ADDR_LEN,
+	spi_flash_command_init(&cmd, CMD_SST_AAI_WP, flash->addr_len,
 			       SPI_FCMD_WRITE);
 	cmd.addr = offset;
 	cmd.data_len = 2;
@@ -1103,6 +1103,9 @@ int spi_flash_scan(struct spi_flash *flash)
 		flash->flags |= SNOR_F_USE_FSR;
 #endif
 
+	/* Set the address length */
+	flash->addr_len = SPI_FLASH_3B_ADDR_LEN;
+
 	/* Configure the BAR - discover bank cmds and read current bank */
 #ifdef CONFIG_SPI_FLASH_BAR
 	ret = read_bar(flash, info);
diff --git a/include/spi_flash.h b/include/spi_flash.h
index ac2b37f0202f..9168fca8f96d 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -44,6 +44,7 @@ struct spi_slave;
  * @bank_read_cmd:	Bank read cmd
  * @bank_write_cmd:	Bank write cmd
  * @bank_curr:		Current flash bank
+ * @addr_len:		Number of bytes for the address
  * @erase_cmd:		Erase cmd 4K, 32K, 64K
  * @read_cmd:		Read cmd - Array Fast, Extn read and quad read.
  * @write_cmd:		Write cmd - page and quad program.
@@ -81,6 +82,7 @@ struct spi_flash {
 	u8 bank_write_cmd;
 	u8 bank_curr;
 #endif
+	u8 addr_len;
 	u8 erase_cmd;
 	u8 read_cmd;
 	u8 write_cmd;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 6/8] sf: add new option to support SPI flash above 16MiB
  2017-05-19 14:59 [U-Boot] [PATCH 0/8] sf: improve support of (Q)SPI flash memories Cyrille Pitchen
                   ` (4 preceding siblings ...)
  2017-05-19 14:59 ` [U-Boot] [PATCH 5/8] sf: add 'addr_len' member to 'struct spi_flash' Cyrille Pitchen
@ 2017-05-19 14:59 ` Cyrille Pitchen
  2017-05-22  1:20   ` Yang, Wenyou
  2017-05-19 14:59 ` [U-Boot] [PATCH 7/8] sf: add support to Microchip SST26 QSPI memories Cyrille Pitchen
  2017-05-19 14:59 ` [U-Boot] [PATCH 8/8] sf: add driver for Atmel QSPI controller Cyrille Pitchen
  7 siblings, 1 reply; 17+ messages in thread
From: Cyrille Pitchen @ 2017-05-19 14:59 UTC (permalink / raw)
  To: u-boot

The patch provides an alternative method to support SPI flash size greater
than 16MiB (128Mib).

Indeed using the Base Address Register (BAR) is stateful. Hence, once the
BAR has been modified, if a spurious CPU reset occurs with no reset/power
off at the SPI flash side, early boot loarders may try to read from offset
0 but fails because of the new BAR value.

On the other hand, using the 4-byte address instruction set is stateless.
When supported by the SPI flash memory, it allows us to access memory data
area above 16MiB without changing the internal state of this SPI flash
memory. Then if a spirious reboot occurs, early boot loaders can still
access data from offset 0.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/spi/Kconfig       | 15 ++++++-
 drivers/mtd/spi/sf_internal.h | 18 +++++++++
 drivers/mtd/spi/spi_flash.c   | 92 ++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 118 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig
index 5ca0a712d84a..c11e83263b16 100644
--- a/drivers/mtd/spi/Kconfig
+++ b/drivers/mtd/spi/Kconfig
@@ -34,14 +34,27 @@ config SPI_FLASH
 
 	  If unsure, say N
 
+choice
+	prompt "Support SPI flash above 16MiB"
+	depends on SPI_FLASH
+	optional
+
 config SPI_FLASH_BAR
 	bool "SPI flash Bank/Extended address register support"
-	depends on SPI_FLASH
 	help
 	  Enable the SPI flash Bank/Extended address register support.
 	  Bank/Extended address registers are used to access the flash
 	  which has size > 16MiB in 3-byte addressing.
 
+config SPI_FLASH_4BAIS
+	bool "SPI flash 4-byte address instruction set support"
+	help
+	  Convert the selected 3-byte address op codes into their associated
+	  4-byte address op codes. Using this instruction set does not change
+	  the internal state of the SPI flash device.
+
+endchoice
+
 if SPI_FLASH
 
 config SPI_FLASH_ATMEL
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index 8b8c951bcc55..30994f9f460c 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -27,6 +27,7 @@ enum spi_nor_option_flags {
 };
 
 #define SPI_FLASH_3B_ADDR_LEN		3
+#define SPI_FLASH_4B_ADDR_LEN		4
 #define SPI_FLASH_CMD_LEN		(1 + SPI_FLASH_3B_ADDR_LEN + 16)
 #define SPI_FLASH_16MB_BOUN		0x1000000
 
@@ -64,6 +65,19 @@ enum spi_nor_option_flags {
 #define CMD_READ_CONFIG			0x35
 #define CMD_FLAG_STATUS			0x70
 
+/* 4-byte address instruction set */
+#define CMD_READ_ARRAY_SLOW_4B		0x13
+#define CMD_READ_ARRAY_FAST_4B		0x0c
+#define CMD_READ_DUAL_OUTPUT_FAST_4B	0x3c
+#define CMD_READ_DUAL_IO_FAST_4B	0xbc
+#define CMD_READ_QUAD_OUTPUT_FAST_4B	0x6c
+#define CMD_READ_QUAD_IO_FAST_4B	0xec
+#define CMD_PAGE_PROGRAM_4B		0x12
+#define CMD_PAGE_PROGRAM_1_1_4_4B	0x34
+#define CMD_PAGE_PROGRAM_1_4_4_4B	0x3e
+#define CMD_ERASE_4K_4B			0x21
+#define CMD_ERASE_64K_4B		0xdc
+
 /* Bank addr access commands */
 #ifdef CONFIG_SPI_FLASH_BAR
 # define CMD_BANKADDR_BRWR		0x17
@@ -133,6 +147,10 @@ struct spi_flash_info {
 #define RD_QUADIO		BIT(6)	/* use Quad IO Read */
 #define RD_DUALIO		BIT(7)	/* use Dual IO Read */
 #define RD_FULL			(RD_QUAD | RD_DUAL | RD_QUADIO | RD_DUALIO)
+#define NO_4BAIS		BIT(8)	/*
+					 * 4-byte address instruction set
+					 * NOT supported
+					 */
 };
 
 extern const struct spi_flash_info spi_flash_ids[];
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index c5e00772f241..695c8555db3f 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -176,6 +176,67 @@ bar_end:
 }
 #endif
 
+#ifdef CONFIG_SPI_FLASH_4BAIS
+static u8 spi_flash_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
+{
+	size_t i;
+
+	for (i = 0; i < size; i++)
+		if (table[i][0] == opcode)
+			return table[i][1];
+
+	/* No conversion found, keep input op code. */
+	return opcode;
+}
+
+static u8 spi_flash_convert_3to4_read(u8 opcode)
+{
+	static const u8 spi_flash_3to4_read[][2] = {
+		{CMD_READ_ARRAY_SLOW,		CMD_READ_ARRAY_SLOW_4B},
+		{CMD_READ_ARRAY_FAST,		CMD_READ_ARRAY_FAST_4B},
+		{CMD_READ_DUAL_OUTPUT_FAST,	CMD_READ_DUAL_OUTPUT_FAST_4B},
+		{CMD_READ_DUAL_IO_FAST,		CMD_READ_DUAL_IO_FAST_4B},
+		{CMD_READ_QUAD_OUTPUT_FAST,	CMD_READ_QUAD_OUTPUT_FAST_4B},
+		{CMD_READ_QUAD_IO_FAST,		CMD_READ_QUAD_IO_FAST_4B},
+	};
+
+	return spi_flash_convert_opcode(opcode, spi_flash_3to4_read,
+					ARRAY_SIZE(spi_flash_3to4_read));
+}
+
+static u8 spi_flash_convert_3to4_write(u8 opcode)
+{
+	static const u8 spi_flash_3to4_write[][2] = {
+		{CMD_PAGE_PROGRAM,		CMD_PAGE_PROGRAM_4B},
+		{CMD_PAGE_PROGRAM_1_1_4,	CMD_PAGE_PROGRAM_1_1_4_4B},
+		{CMD_PAGE_PROGRAM_1_4_4,	CMD_PAGE_PROGRAM_1_4_4_4B},
+	};
+
+	return spi_flash_convert_opcode(opcode, spi_flash_3to4_write,
+					ARRAY_SIZE(spi_flash_3to4_write));
+}
+
+static u8 spi_flash_convert_3to4_erase(u8 opcode)
+{
+	static const u8 spi_flash_3to4_erase[][2] = {
+		{CMD_ERASE_4K,	CMD_ERASE_4K_4B},
+		{CMD_ERASE_64K,	CMD_ERASE_64K_4B},
+	};
+
+	return spi_flash_convert_opcode(opcode, spi_flash_3to4_erase,
+					ARRAY_SIZE(spi_flash_3to4_erase));
+}
+
+static void spi_flash_set_4byte_addr_opcodes(struct spi_flash *flash,
+					     const struct spi_flash_info *info)
+{
+	flash->read_cmd = spi_flash_convert_3to4_read(flash->read_cmd);
+	flash->write_cmd = spi_flash_convert_3to4_write(flash->write_cmd);
+	flash->erase_cmd = spi_flash_convert_3to4_erase(flash->erase_cmd);
+	flash->addr_len = SPI_FLASH_4B_ADDR_LEN;
+}
+#endif
+
 #ifdef CONFIG_SF_DUAL_FLASH
 static void spi_flash_dual(struct spi_flash *flash, u32 *addr)
 {
@@ -966,6 +1027,7 @@ int spi_flash_scan(struct spi_flash *flash)
 {
 	struct spi_slave *spi = flash->spi;
 	const struct spi_flash_info *info = NULL;
+	bool above_16MB;
 	int ret;
 
 	info = spi_flash_read_id(flash);
@@ -1106,6 +1168,26 @@ int spi_flash_scan(struct spi_flash *flash)
 	/* Set the address length */
 	flash->addr_len = SPI_FLASH_3B_ADDR_LEN;
 
+	above_16MB = ((flash->dual_flash == SF_SINGLE_FLASH) &&
+		      (flash->size > SPI_FLASH_16MB_BOUN)) ||
+		     ((flash->dual_flash > SF_SINGLE_FLASH) &&
+		      (flash->size > SPI_FLASH_16MB_BOUN << 1));
+
+	/*
+	 * replace the selected 3-byte address op codes with the associated
+	 * 4-byte address op codes, if needed (flash->size > 16 MiB)
+	 */
+#ifdef CONFIG_SPI_FLASH_4BAIS
+	if (above_16MB) {
+		if (info->flags & NO_4BAIS) {
+			puts("SF: Warning - Only lower 16MiB accessible,");
+			puts(" 4-byte address instruction set not supported\n");
+		} else {
+			spi_flash_set_4byte_addr_opcodes(flash, info);
+		}
+	}
+#endif
+
 	/* Configure the BAR - discover bank cmds and read current bank */
 #ifdef CONFIG_SPI_FLASH_BAR
 	ret = read_bar(flash, info);
@@ -1131,13 +1213,11 @@ int spi_flash_scan(struct spi_flash *flash)
 	puts("\n");
 #endif
 
-#ifndef CONFIG_SPI_FLASH_BAR
-	if (((flash->dual_flash == SF_SINGLE_FLASH) &&
-	     (flash->size > SPI_FLASH_16MB_BOUN)) ||
-	     ((flash->dual_flash > SF_SINGLE_FLASH) &&
-	     (flash->size > SPI_FLASH_16MB_BOUN << 1))) {
+#if !defined(CONFIG_SPI_FLASH_BAR) && !defined(CONFIG_SPI_FLASH_4BAIS)
+	if (above_16MB) {
 		puts("SF: Warning - Only lower 16MiB accessible,");
-		puts(" Full access #define CONFIG_SPI_FLASH_BAR\n");
+		puts(" Full access #define CONFIG_SPI_FLASH_BAR");
+		puts(" or CONFIG_SPI_FLASH_4BAIS\n");
 	}
 #endif
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 7/8] sf: add support to Microchip SST26 QSPI memories
  2017-05-19 14:59 [U-Boot] [PATCH 0/8] sf: improve support of (Q)SPI flash memories Cyrille Pitchen
                   ` (5 preceding siblings ...)
  2017-05-19 14:59 ` [U-Boot] [PATCH 6/8] sf: add new option to support SPI flash above 16MiB Cyrille Pitchen
@ 2017-05-19 14:59 ` Cyrille Pitchen
  2017-05-22  1:20   ` Yang, Wenyou
  2017-05-19 14:59 ` [U-Boot] [PATCH 8/8] sf: add driver for Atmel QSPI controller Cyrille Pitchen
  7 siblings, 1 reply; 17+ messages in thread
From: Cyrille Pitchen @ 2017-05-19 14:59 UTC (permalink / raw)
  To: u-boot

This patch adds support to Microchip SST26 QSPI memories.

Erase blocks are protected at power up and must be unlocked first before
being erased then programmed.

Also, the erase block sizes are not uniform. The memory layout is uniform
only for the 4K sector blocks. The 64K Block Erase (D8h) op code cannot be
used as currently done by the SPI FLASH sub-system.
The 4K Sector Erase (20h) op code should be chosen instead even if
CONFIG_SPI_FLASH_USE_4K_SECTORS is not set.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/spi/sf_internal.h   |  3 +++
 drivers/mtd/spi/spi_flash.c     | 33 ++++++++++++++++++++++++++++++---
 drivers/mtd/spi/spi_flash_ids.c |  5 +++++
 3 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index 30994f9f460c..4354a2aa532f 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -104,6 +104,7 @@ enum spi_nor_option_flags {
 #ifdef CONFIG_SPI_FLASH_SST
 # define CMD_SST_BP		0x02    /* Byte Program */
 # define CMD_SST_AAI_WP		0xAD	/* Auto Address Incr Word Program */
+# define CMD_SST_ULBPR		0x98	/* Global Block Protection Unlock */
 
 int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
 		const void *buf);
@@ -151,6 +152,8 @@ struct spi_flash_info {
 					 * 4-byte address instruction set
 					 * NOT supported
 					 */
+#define SECT_4K_ONLY		BIT(9)  /* use only CMD_ERASE_4K */
+#define SST_ULBPR		BIT(10)	/* use SST unlock block protection */
 };
 
 extern const struct spi_flash_info spi_flash_ids[];
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index 695c8555db3f..307e4140826b 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -891,6 +891,22 @@ int stm_unlock(struct spi_flash *flash, u32 ofs, size_t len)
 }
 #endif
 
+#ifdef CONFIG_SPI_FLASH_SST
+static int sst26_unlock(struct spi_flash *flash)
+{
+	struct spi_flash_command cmd;
+	int ret;
+
+	spi_flash_command_init(&cmd, CMD_SST_ULBPR, 0, SPI_FCMD_WRITE_REG);
+	ret = spi_flash_write_common(flash, &cmd);
+	if (ret) {
+		debug("SF: SST26 is still locked (read-only)\n");
+		return ret;
+	}
+
+	return 0;
+}
+#endif
 
 #ifdef CONFIG_SPI_FLASH_MACRONIX
 static int macronix_quad_enable(struct spi_flash *flash)
@@ -920,7 +936,8 @@ static int macronix_quad_enable(struct spi_flash *flash)
 }
 #endif
 
-#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
+#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND) ||\
+    defined(CONFIG_SPI_FLASH_SST)
 static int spansion_quad_enable(struct spi_flash *flash)
 {
 	u8 qeb_status;
@@ -981,9 +998,11 @@ static int set_quad_mode(struct spi_flash *flash,
 	case SPI_FLASH_CFI_MFR_MACRONIX:
 		return macronix_quad_enable(flash);
 #endif
-#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
+#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND) ||\
+    defined(CONFIG_SPI_FLASH_SST)
 	case SPI_FLASH_CFI_MFR_SPANSION:
 	case SPI_FLASH_CFI_MFR_WINBOND:
+	case SPI_FLASH_CFI_MFR_SST:
 		return spansion_quad_enable(flash);
 #endif
 #ifdef CONFIG_SPI_FLASH_STMICRO
@@ -1040,6 +1059,11 @@ int spi_flash_scan(struct spi_flash *flash)
 	    JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SST)
 		write_sr(flash, 0);
 
+#ifdef CONFIG_SPI_FLASH_SST
+	if (info->flags & SST_ULBPR)
+		sst26_unlock(flash);
+#endif
+
 	flash->name = info->name;
 	flash->memory_map = spi->memory_map;
 
@@ -1099,7 +1123,10 @@ int spi_flash_scan(struct spi_flash *flash)
 		flash->erase_size = 4096 << flash->shift;
 	} else
 #endif
-	{
+	if (info->flags & SECT_4K_ONLY) {
+		flash->erase_cmd = CMD_ERASE_4K;
+		flash->erase_size = 4096 << flash->shift;
+	} else {
 		flash->erase_cmd = CMD_ERASE_64K;
 		flash->erase_size = flash->sector_size;
 	}
diff --git a/drivers/mtd/spi/spi_flash_ids.c b/drivers/mtd/spi/spi_flash_ids.c
index edca94e30cf0..3d3132bc3b22 100644
--- a/drivers/mtd/spi/spi_flash_ids.c
+++ b/drivers/mtd/spi/spi_flash_ids.c
@@ -146,6 +146,11 @@ const struct spi_flash_info spi_flash_ids[] = {
 	{"sst25wf040",	   INFO(0xbf2504, 0x0,	64 * 1024,     8, SECT_4K | SST_WR) },
 	{"sst25wf040b",	   INFO(0x621613, 0x0,	64 * 1024,     8, SECT_4K) },
 	{"sst25wf080",	   INFO(0xbf2505, 0x0,	64 * 1024,    16, SECT_4K | SST_WR) },
+	{"sst26vf016b",	   INFO(0xbf2641, 0x0,   4 * 1024,   512, SECT_4K_ONLY | SST_ULBPR | RD_FULL) },
+	{"sst26vf032b",	   INFO(0xbf2642, 0x0,   4 * 1024,  1024, SECT_4K_ONLY | SST_ULBPR | RD_FULL) },
+	{"sst26vf064b",	   INFO(0xbf2643, 0x0,   4 * 1024,  2048, SECT_4K_ONLY | SST_ULBPR | RD_FULL) },
+	{"sst26wf040b",	   INFO(0xbf2654, 0x0,   4 * 1024,   128, SECT_4K_ONLY | SST_ULBPR | RD_FULL) },
+	{"sst26wf080b",	   INFO(0xbf2658, 0x0,   4 * 1024,   256, SECT_4K_ONLY | SST_ULBPR | RD_FULL) },
 #endif
 #ifdef CONFIG_SPI_FLASH_WINBOND		/* WINBOND */
 	{"w25p80",	   INFO(0xef2014, 0x0,	64 * 1024,    16, 0) },
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 8/8] sf: add driver for Atmel QSPI controller
  2017-05-19 14:59 [U-Boot] [PATCH 0/8] sf: improve support of (Q)SPI flash memories Cyrille Pitchen
                   ` (6 preceding siblings ...)
  2017-05-19 14:59 ` [U-Boot] [PATCH 7/8] sf: add support to Microchip SST26 QSPI memories Cyrille Pitchen
@ 2017-05-19 14:59 ` Cyrille Pitchen
  2017-05-22  1:21   ` Yang, Wenyou
  7 siblings, 1 reply; 17+ messages in thread
From: Cyrille Pitchen @ 2017-05-19 14:59 UTC (permalink / raw)
  To: u-boot

This patch adds support to the Atmel Quad SPI controller.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/spi/Kconfig      |   7 +
 drivers/spi/Makefile     |   1 +
 drivers/spi/atmel_qspi.c | 404 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/atmel_qspi.h | 169 ++++++++++++++++++++
 4 files changed, 581 insertions(+)
 create mode 100644 drivers/spi/atmel_qspi.c
 create mode 100644 drivers/spi/atmel_qspi.h

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index f3f7dbe0897b..73f2e5c26bfb 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -32,6 +32,13 @@ config ATH79_SPI
 	  uses driver model and requires a device tree binding to operate.
 	  please refer to doc/device-tree-bindings/spi/spi-ath79.txt.
 
+config ATMEL_QSPI
+	bool "Atmel QSPI driver"
+	depends on ARCH_AT91
+	help
+	  Enable the Ateml Quad-SPI (QSPI) driver. This driver can only be
+	  used to access SPI NOR flashes.
+
 config ATMEL_SPI
 	bool "Atmel SPI driver"
 	depends on ARCH_AT91
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index c090562c7732..af75fa41c82f 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -18,6 +18,7 @@ endif
 obj-$(CONFIG_ALTERA_SPI) += altera_spi.o
 obj-$(CONFIG_ATH79_SPI) += ath79_spi.o
 obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o
+obj-$(CONFIG_ATMEL_QSPI) += atmel_qspi.o
 obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o
 obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
 obj-$(CONFIG_CF_SPI) += cf_spi.o
diff --git a/drivers/spi/atmel_qspi.c b/drivers/spi/atmel_qspi.c
new file mode 100644
index 000000000000..6c265d0a4714
--- /dev/null
+++ b/drivers/spi/atmel_qspi.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2017 Atmel Corporation
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <spi.h>
+#include <asm/io.h>
+#include <mach/clk.h>
+#include "atmel_qspi.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static void atmel_qspi_memcpy_fromio(void *dst, unsigned long src, size_t len)
+{
+	u8 *d = (u8 *)dst;
+
+	while (len--) {
+		*d++ = readb(src);
+		src++;
+	}
+}
+
+static void atmel_qspi_memcpy_toio(unsigned long dst, const void *src,
+				   size_t len)
+{
+	const u8 *s = (const u8 *)src;
+
+	while (len--) {
+		writeb(*s, dst);
+		dst++;
+		s++;
+	}
+}
+
+static int atmel_qspi_set_ifr_tfrtype(u8 flags, u32 *ifr)
+{
+	u32 ifr_tfrtype;
+
+	switch (flags & SPI_FCMD_TYPE) {
+	case SPI_FCMD_READ:
+		ifr_tfrtype = QSPI_IFR_TFRTYPE_READ_MEMORY;
+		break;
+
+	case SPI_FCMD_WRITE:
+		ifr_tfrtype = QSPI_IFR_TFRTYPE_WRITE_MEMORY;
+		break;
+
+	case SPI_FCMD_ERASE:
+	case SPI_FCMD_WRITE_REG:
+		ifr_tfrtype = QSPI_IFR_TFRTYPE_WRITE;
+		break;
+
+	case SPI_FCMD_READ_REG:
+		ifr_tfrtype = QSPI_IFR_TFRTYPE_READ;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	*ifr = (*ifr & ~QSPI_IFR_TFRTYPE) | ifr_tfrtype;
+	return 0;
+}
+
+static int atmel_qpsi_set_ifr_width(enum spi_flash_protocol proto, u32 *ifr)
+{
+	u32 ifr_width;
+
+	switch (proto) {
+	case SPI_FPROTO_1_1_1:
+		ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
+		break;
+
+	case SPI_FPROTO_1_1_2:
+		ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
+		break;
+
+	case SPI_FPROTO_1_2_2:
+		ifr_width = QSPI_IFR_WIDTH_DUAL_IO;
+		break;
+
+	case SPI_FPROTO_2_2_2:
+		ifr_width = QSPI_IFR_WIDTH_DUAL_CMD;
+		break;
+
+	case SPI_FPROTO_1_1_4:
+		ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
+		break;
+
+	case SPI_FPROTO_1_4_4:
+		ifr_width = QSPI_IFR_WIDTH_QUAD_IO;
+		break;
+
+	case SPI_FPROTO_4_4_4:
+		ifr_width = QSPI_IFR_WIDTH_QUAD_CMD;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	*ifr = (*ifr & ~QSPI_IFR_WIDTH) | ifr_width;
+	return 0;
+}
+
+static int atmel_qspi_xfer(struct udevice *dev, unsigned int bitlen,
+			   const void *dout, void *din, unsigned long flags)
+{
+	/* This controller can only be used with SPI NOR flashes. */
+	return -EINVAL;
+}
+
+static int atmel_qspi_set_speed(struct udevice *bus, uint hz)
+{
+	struct atmel_qspi_priv *aq = dev_get_priv(bus);
+	u32 scr, scbr, mask, new_value;
+
+	/* Compute the QSPI baudrate */
+	scbr = DIV_ROUND_UP(aq->bus_clk_rate, hz);
+	if (scbr > 0)
+		scbr--;
+
+	new_value = QSPI_SCR_SCBR_(scbr);
+	mask = QSPI_SCR_SCBR;
+
+	scr = qspi_readl(aq, QSPI_SCR);
+	if ((scr & mask) == new_value)
+		return 0;
+
+	scr = (scr & ~mask) | new_value;
+	qspi_writel(aq, QSPI_SCR, scr);
+
+	return 0;
+}
+
+static int atmel_qspi_set_mode(struct udevice *bus, uint mode)
+{
+	struct atmel_qspi_priv *aq = dev_get_priv(bus);
+	u32 scr, mask, new_value;
+
+	new_value = (QSPI_SCR_CPOL_((mode & SPI_CPOL) != 0) |
+		     QSPI_SCR_CPHA_((mode & SPI_CPHA) != 0));
+	mask = (QSPI_SCR_CPOL | QSPI_SCR_CPHA);
+
+	scr = qspi_readl(aq, QSPI_SCR);
+	if ((scr & mask) == new_value)
+		return 0;
+
+	scr = (scr & ~mask) | new_value;
+	qspi_writel(aq, QSPI_SCR, scr);
+
+	return 0;
+}
+
+static bool
+atmel_qspi_is_flash_command_supported(struct udevice *dev,
+				      const struct spi_flash_command *cmd)
+{
+	return true;
+}
+
+static int atmel_qspi_exec_flash_command(struct udevice *dev,
+					 const struct spi_flash_command *cmd)
+{
+	struct udevice *bus = dev_get_parent(dev);
+	struct atmel_qspi_priv *aq = dev_get_priv(bus);
+	unsigned int iar, icr, ifr;
+	unsigned int offset;
+	unsigned int imr, sr;
+	unsigned long memaddr;
+	int err;
+
+	iar = 0;
+	icr = 0;
+	ifr = 0;
+
+	err = atmel_qspi_set_ifr_tfrtype(cmd->flags, &ifr);
+	if (err)
+		return err;
+
+	err = atmel_qpsi_set_ifr_width(cmd->proto, &ifr);
+	if (err)
+		return err;
+
+	/* Compute instruction parameters */
+	icr |= QSPI_ICR_INST_(cmd->inst);
+	ifr |= QSPI_IFR_INSTEN;
+
+	/* Compute address parameters. */
+	switch (cmd->addr_len) {
+	case 4:
+		ifr |= QSPI_IFR_ADDRL_32_BIT;
+		/*break;*/ /* fall through the 24bit (3 byte) address case */
+	case 3:
+		iar = cmd->data_len ? 0 : cmd->addr;
+		ifr |= QSPI_IFR_ADDREN;
+		offset = cmd->addr;
+		break;
+	case 0:
+		offset = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Compute option parameters. */
+	if (cmd->num_mode_cycles) {
+		unsigned int mode_cycle_bits, mode_bits;
+
+		icr |= QSPI_ICR_OPT_(cmd->mode);
+		ifr |= QSPI_IFR_OPTEN;
+
+		switch (ifr & QSPI_IFR_WIDTH) {
+		case QSPI_IFR_WIDTH_SINGLE_BIT_SPI:
+		case QSPI_IFR_WIDTH_DUAL_OUTPUT:
+		case QSPI_IFR_WIDTH_QUAD_OUTPUT:
+			mode_cycle_bits = 1;
+			break;
+		case QSPI_IFR_WIDTH_DUAL_IO:
+		case QSPI_IFR_WIDTH_DUAL_CMD:
+			mode_cycle_bits = 2;
+			break;
+		case QSPI_IFR_WIDTH_QUAD_IO:
+		case QSPI_IFR_WIDTH_QUAD_CMD:
+			mode_cycle_bits = 4;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		mode_bits = cmd->num_mode_cycles * mode_cycle_bits;
+		switch (mode_bits) {
+		case 1:
+			ifr |= QSPI_IFR_OPTL_1BIT;
+			break;
+
+		case 2:
+			ifr |= QSPI_IFR_OPTL_2BIT;
+			break;
+
+		case 4:
+			ifr |= QSPI_IFR_OPTL_4BIT;
+			break;
+
+		case 8:
+			ifr |= QSPI_IFR_OPTL_8BIT;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* Set the number of dummy cycles. */
+	if (cmd->num_wait_states)
+		ifr |= QSPI_IFR_NBDUM_(cmd->num_wait_states);
+
+	/* Set data enable. */
+	if (cmd->data_len)
+		ifr |= QSPI_IFR_DATAEN;
+
+	/* Clear pending interrupts. */
+	(void)qspi_readl(aq, QSPI_SR);
+
+	/* Set QSPI Instruction Frame registers. */
+	qspi_writel(aq, QSPI_IAR, iar);
+	qspi_writel(aq, QSPI_ICR, icr);
+	qspi_writel(aq, QSPI_IFR, ifr);
+
+	/* Skip to the final steps if there is no data. */
+	if (!cmd->data_len)
+		goto no_data;
+
+	/* Dummy read of QSPI_IFR to synchronize APB and AHB accesses. */
+	(void)qspi_readl(aq, QSPI_IFR);
+
+	/* Stop here for Continuous Read. */
+	memaddr = (unsigned long)(aq->membase + offset);
+	if (cmd->tx_data)
+		/* Write data. */
+		atmel_qspi_memcpy_toio(memaddr, cmd->tx_data, cmd->data_len);
+	else if (cmd->rx_data)
+		/* Read data. */
+		atmel_qspi_memcpy_fromio(cmd->rx_data, memaddr, cmd->data_len);
+
+	/* Release the chip-select. */
+	qspi_writel(aq, QSPI_CR, QSPI_CR_LASTXFER);
+
+no_data:
+	/* Poll INSTruction End and Chip Select Rise flags. */
+	imr = QSPI_SR_INSTRE | QSPI_SR_CSR;
+	sr = 0;
+	while (sr != (QSPI_SR_INSTRE | QSPI_SR_CSR))
+		sr |= qspi_readl(aq, QSPI_SR) & imr;
+
+	return 0;
+}
+
+
+static const struct dm_spi_ops atmel_qspi_ops = {
+	.xfer				= atmel_qspi_xfer,
+	.set_speed			= atmel_qspi_set_speed,
+	.set_mode			= atmel_qspi_set_mode,
+	.is_flash_command_supported	= atmel_qspi_is_flash_command_supported,
+	.exec_flash_command		= atmel_qspi_exec_flash_command,
+};
+
+static int atmel_qspi_enable_clk(struct udevice *bus)
+{
+	struct atmel_qspi_priv *aq = dev_get_priv(bus);
+	struct clk clk;
+	ulong clk_rate;
+	int ret;
+
+	ret = clk_get_by_index(bus, 0, &clk);
+	if (ret)
+		return -EINVAL;
+
+	ret = clk_enable(&clk);
+	if (ret)
+		goto free_clock;
+
+	clk_rate = clk_get_rate(&clk);
+	if (!clk_rate) {
+		ret = -EINVAL;
+		goto free_clock;
+	}
+
+	aq->bus_clk_rate = clk_rate;
+
+free_clock:
+	clk_free(&clk);
+
+	return ret;
+}
+
+static int atmel_qspi_probe(struct udevice *bus)
+{
+	const struct atmel_qspi_platdata *plat = dev_get_platdata(bus);
+	struct atmel_qspi_priv *aq = dev_get_priv(bus);
+	u32 mr;
+	int ret;
+
+	ret = atmel_qspi_enable_clk(bus);
+	if (ret)
+		return ret;
+
+	aq->regbase = plat->regbase;
+	aq->membase = plat->membase;
+
+	/* Reset the QSPI controler */
+	qspi_writel(aq, QSPI_CR, QSPI_CR_SWRST);
+
+	/* Set the QSPI controller in Serial Memory Mode */
+	mr = (QSPI_MR_NBBITS_8_BIT |
+	      QSPI_MR_SMM_MEMORY |
+	      QSPI_MR_CSMODE_LASTXFER);
+	qspi_writel(aq, QSPI_MR, mr);
+
+	/* Enable the QSPI controller */
+	qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIEN);
+
+	return 0;
+}
+
+static int atmel_qspi_ofdata_to_platdata(struct udevice *bus)
+{
+	struct atmel_qspi_platdata *plat = dev_get_platdata(bus);
+	const void *blob = gd->fdt_blob;
+	int node = bus->of_offset;
+	u32 data[4];
+	int ret;
+
+	ret = fdtdec_get_int_array(blob, node, "reg", data, ARRAY_SIZE(data));
+	if (ret) {
+		printf("Error: Can't get base addresses (ret=%d)!\n", ret);
+		return -ENODEV;
+	}
+	plat->regbase = (void *)data[0];
+	plat->membase = (void *)data[2];
+
+	return 0;
+}
+
+static const struct udevice_id atmel_qspi_ids[] = {
+	{ .compatible = "atmel,sama5d2-qspi" },
+	{ }
+};
+
+U_BOOT_DRIVER(atmel_qspi) = {
+	.name		= "atmel_qspi",
+	.id		= UCLASS_SPI,
+	.of_match	= atmel_qspi_ids,
+	.ops		= &atmel_qspi_ops,
+	.ofdata_to_platdata = atmel_qspi_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct atmel_qspi_platdata),
+	.priv_auto_alloc_size = sizeof(struct atmel_qspi_priv),
+	.probe		= atmel_qspi_probe,
+};
diff --git a/drivers/spi/atmel_qspi.h b/drivers/spi/atmel_qspi.h
new file mode 100644
index 000000000000..ee1a14bd726d
--- /dev/null
+++ b/drivers/spi/atmel_qspi.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __ATMEL_QSPI_H__
+#define __ATMEL_QSPI_H__
+
+/*
+ * Register Definitions
+ */
+#define	QSPI_CR		0x00	/* Control Register */
+#define	QSPI_MR		0x04	/* Mode Register */
+#define	QSPI_RDR	0x08	/* Receive Data Register */
+#define	QSPI_TDR	0x0c	/* Transmit Data Register */
+#define	QSPI_SR		0x10	/* Status Register */
+#define	QSPI_IER	0x14	/* Interrupt Enable Register */
+#define	QSPI_IDR	0x18	/* Interrupt Disable Register */
+#define	QSPI_IMR	0x1c	/* Interrupt Mask Register */
+#define	QSPI_SCR	0x20	/* Serial Clock Register */
+#define	QSPI_IAR	0x30	/* Instruction Address Register */
+#define	QSPI_ICR	0x34	/* Instruction Code Register */
+#define	QSPI_IFR	0x38	/* Instruction Frame Register */
+/* 0x3c Reserved */
+#define	QSPI_SMR	0x40	/* Scrambling Mode Register */
+#define	QSPI_SKR	0x44	/* Scrambling Key Register */
+/* 0x48 ~ 0xe0 */
+#define	QSPI_WPMR	0xe4	/* Write Protection Mode Register */
+#define	QSPI_WPSR	0xe8	/* Write Protection Status Register */
+/* 0xec ~ 0xf8 Reserved */
+/* 0xfc Reserved */
+
+/*
+ * Register Field Definitions
+ */
+/* QSPI_CR */
+#define	QSPI_CR_QSPIEN		BIT(0)	/* QSPI Enable */
+#define	QSPI_CR_QSPIDIS		BIT(1)	/* QSPI Disable */
+#define	QSPI_CR_SWRST		BIT(7)	/* QSPI Software Reset */
+#define	QSPI_CR_LASTXFER	BIT(24)	/* Last Transfer */
+
+/* QSPI_MR */
+#define	QSPI_MR_SMM		BIT(0)	/* Serial Memort Mode */
+#define		QSPI_MR_SMM_SPI		0
+#define		QSPI_MR_SMM_MEMORY	QSPI_MR_SMM
+#define	QSPI_MR_LLB		BIT(1)	/* Local Localback Enable */
+#define		QSPI_MR_LLB_DISABLED	0
+#define		QSPI_MR_LLB_ENABLED	QSPI_MR_LLB
+#define	QSPI_MR_WDRBT		BIT(2)	/* Wait Data Read Before Transfer */
+#define		QSPI_MR_WDRBT_DISABLED	0
+#define		QSPI_MR_WDRBT_ENABLED	QSPI_MR_WDRBT
+#define	QSPI_MR_SMRM		BIT(3)	/* Serial Memory Register Mode */
+#define		QSPI_MR_SMRM_AHB	0
+#define		QSPI_MR_SMRM_APB	QSPI_MR_SMRM
+#define	QSPI_MR_CSMODE		GENMASK(5, 4)	/* Chip Select Mode */
+#define		QSPI_MR_CSMODE_NOT_RELOADED	(0x0u << 4)
+#define		QSPI_MR_CSMODE_LASTXFER		(0x1u << 4)
+#define		QSPI_MR_CSMODE_SYSTEMATICALLY	(0x2u << 4)
+#define	QSPI_MR_NBBITS		GENMASK(11, 8)	/*
+						 * Number of Bits Per
+						 * Transfer
+						 */
+#define		QSPI_MR_NBBITS_8_BIT		(0x0u << 8)
+#define		QSPI_MR_NBBITS_16_BIT		(0x8u << 8)
+#define	QSPI_MR_DLYBCT		GENMASK(23, 16)	/*
+						 * Delay Between Consecutive
+						 * Transfers
+						 */
+#define	QSPI_MR_DLYCS		GENMASK(31, 24)	/* Minimum Inactive QCS Delay */
+
+/* QSPI_SR */
+#define	QSPI_SR_RDRF		BIT(0)	/* Receive Data Register Full */
+#define	QSPI_SR_TDRE		BIT(1)	/* Transmit Data Register Empty */
+#define	QSPI_SR_TXEMPTY		BIT(2)	/* Transmission Registers Empty */
+#define	QSPI_SR_OVRES		BIT(3)	/* Overrun Error Status */
+#define	QSPI_SR_CSR		BIT(8)	/* Chip Select Rise */
+#define	QSPI_SR_CSS		BIT(9)	/* Chip Select Status */
+#define	QSPI_SR_INSTRE		BIT(10)	/* Instruction End Status */
+#define	QSPI_SR_QSPIENS		BIT(24)	/* QSPI Enable Status */
+
+/* QSPI_SCR */
+#define	QSPI_SCR_CPOL		BIT(0)	/* Clock Polarity */
+#define	QSPI_SCR_CPOL_(x)	((x) << 0)
+#define	QSPI_SCR_CPHA		BIT(1)	/* Clock Phase */
+#define	QSPI_SCR_CPHA_(x)	((x) << 1)
+#define	QSPI_SCR_SCBR		GENMASK(15, 8)	/* Serial Clock Baud Rate */
+#define	QSPI_SCR_SCBR_(x)	(((x) << 8) & QSPI_SCR_SCBR)
+#define QSPI_SCR_DLYBS		GENMASK(23, 16)
+#define	QSPI_SCR_DLYBS_(x)	(((x) << 16) & QSPI_SCR_DLYBS)	/*
+								 * Delay Before
+								 * QSCK
+								 */
+
+/* QSPI_ICR */
+#define QSPI_ICR_INST		GENMASK(7, 0)
+#define	QSPI_ICR_INST_(x)	(((x) << 0) & QSPI_ICR_INST)	/*
+								 * Instruction
+								 * Code
+								 */
+#define QSPI_ICR_OPT		GENMASK(23, 16)
+#define	QSPI_ICR_OPT_(x)	(((x) << 16) & QSPI_ICR_OPT)	/*
+								 * Option
+								 * Code
+								 */
+
+/* QSPI_IFR */
+#define	QSPI_IFR_WIDTH		GENMASK(2, 0)	/*
+						 * Width of Instruction Code,
+						 * Address, Option Code and Data
+						 */
+#define		QSPI_IFR_WIDTH_SINGLE_BIT_SPI	(0x0u << 0)
+#define		QSPI_IFR_WIDTH_DUAL_OUTPUT	(0x1u << 0)
+#define		QSPI_IFR_WIDTH_QUAD_OUTPUT	(0x2u << 0)
+#define		QSPI_IFR_WIDTH_DUAL_IO		(0x3u << 0)
+#define		QSPI_IFR_WIDTH_QUAD_IO		(0x4u << 0)
+#define		QSPI_IFR_WIDTH_DUAL_CMD		(0x5u << 0)
+#define		QSPI_IFR_WIDTH_QUAD_CMD		(0x6u << 0)
+#define QSPI_IFR_WIDTH_(x)	(((x) << 0) & QSPI_IFR_WIDTH)
+#define	QSPI_IFR_INSTEN		BIT(4)	/* Instruction Enable*/
+#define	QSPI_IFR_ADDREN		BIT(5)	/* Address Enable*/
+#define	QSPI_IFR_OPTEN		BIT(6)	/* Option Enable*/
+#define	QSPI_IFR_DATAEN		BIT(7)	/* Data Enable*/
+#define	QSPI_IFR_OPTL		GENMASK(9, 8)	/* Option Code Length */
+#define		QSPI_IFR_OPTL_1BIT		(0x0u << 8)
+#define		QSPI_IFR_OPTL_2BIT		(0x1u << 8)
+#define		QSPI_IFR_OPTL_4BIT		(0x2u << 8)
+#define		QSPI_IFR_OPTL_8BIT		(0x3u << 8)
+#define	QSPI_IFR_ADDRL		BIT(10)	/* Address Length */
+#define		QSPI_IFR_ADDRL_24_BIT		0
+#define		QSPI_IFR_ADDRL_32_BIT		QSPI_IFR_ADDRL
+#define	QSPI_IFR_TFRTYPE	GENMASK(13, 12)	/* Data Transfer Type */
+#define		QSPI_IFR_TFRTYPE_READ		(0x0u << 12)
+#define		QSPI_IFR_TFRTYPE_READ_MEMORY	(0x1u << 12)
+#define		QSPI_IFR_TFRTYPE_WRITE		(0x2u << 12)
+#define		QSPI_IFR_TFRTYPE_WRITE_MEMORY	(0x3u << 12)
+#define QSPI_IFR_TFRTYPE_(x)	(((x) << 12) & QSPI_IFR_TFRTYPE)
+#define	QSPI_IFR_CRM		BIT(14)	/* Continuous Read Mode */
+#define QSPI_IFR_NBDUM		GENMASK(20, 16)
+#define	QSPI_IFR_NBDUM_(x)	(((x) << 16) & QSPI_IFR_NBDUM)	/*
+								 * Number Of
+								 * Dummy Cycles
+								 */
+
+
+struct atmel_qspi_platdata {
+	void		*regbase;
+	void		*membase;
+};
+
+struct atmel_qspi_priv {
+	ulong		bus_clk_rate;
+	void		*regbase;
+	void		*membase;
+};
+
+#include <asm/io.h>
+
+static inline u32 qspi_readl(struct atmel_qspi_priv *aq, u32 reg)
+{
+	return readl(aq->regbase + reg);
+}
+
+static inline void qspi_writel(struct atmel_qspi_priv *aq, u32 reg, u32 value)
+{
+	writel(value, aq->regbase + reg);
+}
+
+#endif /* __ATMEL_QSPI_H__ */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 1/8] spi: add support of SPI flash commands
  2017-05-19 14:59 ` [U-Boot] [PATCH 1/8] spi: add support of SPI flash commands Cyrille Pitchen
@ 2017-05-22  1:17   ` Yang, Wenyou
  0 siblings, 0 replies; 17+ messages in thread
From: Yang, Wenyou @ 2017-05-22  1:17 UTC (permalink / raw)
  To: u-boot



On 2017/5/19 22:59, Cyrille Pitchen wrote:
> This patch introduces 'struct spi_flash_command' and functions
> spi_is_flash_command_supported() / spi_exec_flash_command().
>
> The 'struct spi_flash_command' describes all the relevant parameters to
> execute any SPI flash command:
> - the instruction op code
> - the number of bytes used to send the address: 0, 3 or 4 bytes
> - the number of mode and wait-state clock cycles, also called dummy cycles
> - the number and values of data bytes to be sent or received
> - the SPI x-y-z protocol [1]
> - the flash command type [2]
>
> [1] SPI x-y-z protocol:
> - x is the number of I/O lines used to send the instruction op code.
> - y is the number of I/O lines used during address, mode and wait-state
>    clock cycles.
> - z is the number of I/O lines used to send or received data.
>
> [2] Flash command type:
> The flash command type is provided to differenciate "memory"
> read/write/erase operations from "flash internal register" read/write
> operations. Indeed some SPI controller drivers handle those command type
> in different ways.
> However SPI controller drivers should not check the value of the
> instruction op code to guess the actual kind of flash command to perform.
> Many instruction op codes are SPI flash manufacturer specific and only
> drivers/mtd/spi/spi_flash.c should have the knowledge of all of them.
>
> Besides, more and more QSPI controllers, like those of TI and Candence,
> have special way to support (Fast) Read operations using some
> "memory like" area mapped into the system bus. Hence, if those drivers
> choose to override the default implementation of
> spi_is_flash_command_supported() so that their own functions return true
> only for a "memory read" flash command type, then spi_exec_flash_command()
> might be used to implement the read from the "memory like" area mapped
> into the system bus.
> It means that spi_exec_flash_command() could be used to supersede the
> actual flash->memory_map mechanism; spi_is_flash_command_supported() /
> spi_exec_flash_command() being more generic and covering more use cases.
>
> For instance, the Atmel QSPI hardware controller uses its "memory like"
> area mapped ino the system to perform not only (Fast) Read operations but
> actually all other types of flash commands. Hence the regular SPI API
> based on the spi_xfer() function is not suited to support the Atmel QSPI
> controller.
>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

Acked-by Wenyou Yang <wenyou.yang@atmel.com>


Best Regards,
Wenyou Yang

> ---
>   drivers/spi/spi-uclass.c |  40 +++++++++++
>   drivers/spi/spi.c        |  13 ++++
>   include/spi.h            | 168 +++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 221 insertions(+)
>
> diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c
> index c061c05443d4..b8092538e9b0 100644
> --- a/drivers/spi/spi-uclass.c
> +++ b/drivers/spi/spi-uclass.c
> @@ -92,6 +92,30 @@ int dm_spi_xfer(struct udevice *dev, unsigned int bitlen,
>   	return spi_get_ops(bus)->xfer(dev, bitlen, dout, din, flags);
>   }
>   
> +bool dm_spi_is_flash_command_supported(struct udevice *dev,
> +				       const struct spi_flash_command *cmd)
> +{
> +	struct udevice *bus = dev->parent;
> +	struct dm_spi_ops *ops = spi_get_ops(bus);
> +
> +	if (ops->is_flash_command_supported)
> +		return ops->is_flash_command_supported(dev, cmd);
> +
> +	return false;
> +}
> +
> +int dm_spi_exec_flash_command(struct udevice *dev,
> +			      const struct spi_flash_command *cmd)
> +{
> +	struct udevice *bus = dev->parent;
> +	struct dm_spi_ops *ops = spi_get_ops(bus);
> +
> +	if (ops->exec_flash_command)
> +		return ops->exec_flash_command(dev, cmd);
> +
> +	return -EINVAL;
> +}
> +
>   int spi_claim_bus(struct spi_slave *slave)
>   {
>   	return dm_spi_claim_bus(slave->dev);
> @@ -108,6 +132,18 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
>   	return dm_spi_xfer(slave->dev, bitlen, dout, din, flags);
>   }
>   
> +bool spi_is_flash_command_supported(struct spi_slave *slave,
> +				    const struct spi_flash_command *cmd)
> +{
> +	return dm_spi_is_flash_command_supported(slave->dev, cmd);
> +}
> +
> +int spi_exec_flash_command(struct spi_slave *slave,
> +			   const struct spi_flash_command *cmd)
> +{
> +	return dm_spi_exec_flash_command(slave->dev, cmd);
> +}
> +
>   #if !CONFIG_IS_ENABLED(OF_PLATDATA)
>   static int spi_child_post_bind(struct udevice *dev)
>   {
> @@ -147,6 +183,10 @@ static int spi_post_probe(struct udevice *bus)
>   		ops->set_mode += gd->reloc_off;
>   	if (ops->cs_info)
>   		ops->cs_info += gd->reloc_off;
> +	if (ops->is_flash_command_supported)
> +		ops->is_flash_command_supported += gd->reloc_off;
> +	if (ops->exec_flash_command)
> +		ops->exec_flash_command += gd->reloc_off;
>   #endif
>   
>   	return 0;
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index 7d81fbd7f8f5..e47acdc9e414 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -5,6 +5,7 @@
>    */
>   
>   #include <common.h>
> +#include <errno.h>
>   #include <fdtdec.h>
>   #include <malloc.h>
>   #include <spi.h>
> @@ -58,3 +59,15 @@ struct spi_slave *spi_base_setup_slave_fdt(const void *blob, int busnum,
>   	return spi_setup_slave(busnum, cs, max_hz, mode);
>   }
>   #endif
> +
> +__weak bool spi_is_flash_command_supported(struct spi_slave *slave,
> +					   const struct spi_flash_command *cmd)
> +{
> +	return false;
> +}
> +
> +__weak int spi_exec_flash_command(struct spi_slave *slave,
> +				  const struct spi_flash_command *cmd)
> +{
> +	return -EINVAL;
> +}
> diff --git a/include/spi.h b/include/spi.h
> index deb65efdfb73..eec36b46897b 100644
> --- a/include/spi.h
> +++ b/include/spi.h
> @@ -10,6 +10,8 @@
>   #ifndef _SPI_H_
>   #define _SPI_H_
>   
> +#include <linux/string.h> /* memset() */
> +
>   /* SPI mode flags */
>   #define SPI_CPHA	BIT(0)			/* clock phase */
>   #define SPI_CPOL	BIT(1)			/* clock polarity */
> @@ -64,6 +66,116 @@ struct dm_spi_slave_platdata {
>   #endif /* CONFIG_DM_SPI */
>   
>   /**
> + * enum spi_flash_protocol - SPI flash command protocol
> + */
> +#define SPI_FPROTO_INST_SHIFT	16
> +#define SPI_FPROTO_INST_MASK	GENMASK(23, 16)
> +#define SPI_FPROTO_INST(nbits)					\
> +	((((unsigned long)(nbits)) << SPI_FPROTO_INST_SHIFT) &	\
> +	 SPI_FPROTO_INST_MASK)
> +
> +#define SPI_FPROTO_ADDR_SHIFT	8
> +#define SPI_FPROTO_ADDR_MASK	GENMASK(15, 8)
> +#define SPI_FPROTO_ADDR(nbits)					\
> +	((((unsigned long)(nbits)) << SPI_FPROTO_ADDR_SHIFT) &	\
> +	 SPI_FPROTO_ADDR_MASK)
> +
> +#define SPI_FPROTO_DATA_SHIFT	0
> +#define SPI_FPROTO_DATA_MASK	GENMASK(7, 0)
> +#define SPI_FPROTO_DATA(nbits)					\
> +	((((unsigned long)(nbits)) << SPI_FPROTO_DATA_SHIFT) &	\
> +	 SPI_FPROTO_DATA_MASK)
> +
> +#define SPI_FPROTO(inst_nbits, addr_nbits, data_nbits)	\
> +	(SPI_FPROTO_INST(inst_nbits) |			\
> +	 SPI_FPROTO_ADDR(addr_nbits) |			\
> +	 SPI_FPROTO_DATA(data_nbits))
> +
> +enum spi_flash_protocol {
> +	SPI_FPROTO_1_1_1 = SPI_FPROTO(1, 1, 1),
> +	SPI_FPROTO_1_1_2 = SPI_FPROTO(1, 1, 2),
> +	SPI_FPROTO_1_1_4 = SPI_FPROTO(1, 1, 4),
> +	SPI_FPROTO_1_2_2 = SPI_FPROTO(1, 2, 2),
> +	SPI_FPROTO_1_4_4 = SPI_FPROTO(1, 4, 4),
> +	SPI_FPROTO_2_2_2 = SPI_FPROTO(2, 2, 2),
> +	SPI_FPROTO_4_4_4 = SPI_FPROTO(4, 4, 4),
> +};
> +
> +static inline
> +u8 spi_flash_protocol_get_inst_nbits(enum spi_flash_protocol proto)
> +{
> +	return ((unsigned long)(proto & SPI_FPROTO_INST_MASK)) >>
> +		SPI_FPROTO_INST_SHIFT;
> +}
> +
> +static inline
> +u8 spi_flash_protocol_get_addr_nbits(enum spi_flash_protocol proto)
> +{
> +	return ((unsigned long)(proto & SPI_FPROTO_ADDR_MASK)) >>
> +		SPI_FPROTO_ADDR_SHIFT;
> +}
> +
> +static inline
> +u8 spi_flash_protocol_get_data_nbits(enum spi_flash_protocol proto)
> +{
> +	return ((unsigned long)(proto & SPI_FPROTO_DATA_MASK)) >>
> +		SPI_FPROTO_DATA_SHIFT;
> +}
> +
> +/**
> + * struct spi_flash_command - SPI flash command structure
> + *
> + * @instr:		Opcode sent to the SPI slave during instr clock cycles.
> + * @mode:		Value sent to the SPI slave during mode clock cycles.
> + * @num_mode_cycles:	Number of mode clock cycles.
> + * @num_wait_states:	Number of wait-state clock cycles.
> + * @addr_len:		Number of bytes sent during address clock cycles:
> + *			should be 0, 3, or 4.
> + * @addr:		Value sent to the SPI slave during address clock cycles.
> + * @data_len:		Number of bytes to be sent during data clock cycles.
> + * @tx_data:		Data sent to the SPI slave during data clock cycles.
> + * @rx_data:		Data read from the SPI slave during data clock cycles.
> + */
> +struct spi_flash_command {
> +	enum spi_flash_protocol proto;
> +	u8 flags;
> +#define SPI_FCMD_TYPE		GENMASK(2, 0)
> +#define SPI_FCMD_READ		(0x0U << 0)
> +#define SPI_FCMD_WRITE		(0x1U << 0)
> +#define SPI_FCMD_ERASE		(0x2U << 0)
> +#define SPI_FCMD_READ_REG	(0x3U << 0)
> +#define SPI_FCMD_WRITE_REG	(0x4U << 0)
> +
> +	u8 inst;
> +	u8 mode;
> +	u8 num_mode_cycles;
> +	u8 num_wait_states;
> +	u8 addr_len;
> +	u32 addr;
> +	size_t data_len;
> +	const void *tx_data;
> +	void *rx_data;
> +};
> +
> +/**
> + * Initialize a 'struct spi_flash_command'.
> + *
> + * @cmd:		Pointer to the 'struct spi_flash_command' to initialize.
> + * @instr:		Instruction opcode.
> + * @addr_len:		Number of address bytes.
> + */
> +static inline void
> +spi_flash_command_init(struct spi_flash_command *cmd,
> +		       u8 inst, u8 addr_len, u8 flags)
> +{
> +	memset(cmd, 0, sizeof(*cmd));
> +	cmd->proto = SPI_FPROTO_1_1_1;
> +	cmd->inst = inst;
> +	cmd->addr_len = addr_len;
> +	cmd->flags = flags;
> +}
> +
> +/**
>    * struct spi_slave - Representation of a SPI slave
>    *
>    * For driver model this is the per-child data used by the SPI bus. It can
> @@ -252,6 +364,24 @@ int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen);
>   int  spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
>   		void *din, unsigned long flags);
>   
> +/**
> + * Check whether the given SPI flash command is supported
> + *
> + * @slave:	The SPI slave
> + * @cmd:	The SPI flash command to check.
> + */
> +bool spi_is_flash_command_supported(struct spi_slave *slave,
> +				    const struct spi_flash_command *cmd);
> +
> +/**
> + * Execute SPI flash command
> + *
> + * @slave:	The SPI slave which will execute the give SPI flash command.
> + * @cmd:	The SPI flash command to execute.
> + */
> +int spi_exec_flash_command(struct spi_slave *slave,
> +			   const struct spi_flash_command *cmd);
> +
>   /* Copy memory mapped data */
>   void spi_flash_copy_mmap(void *data, void *offset, size_t len);
>   
> @@ -464,6 +594,26 @@ struct dm_spi_ops {
>   	 *	   is invalid, other -ve value on error
>   	 */
>   	int (*cs_info)(struct udevice *bus, uint cs, struct spi_cs_info *info);
> +
> +	/**
> +	 * Check whether the given SPI flash command is supported.
> +	 *
> +	 * @bus:	The SPI bus
> +	 * @cmd:	The SPI flash command to check.
> +	 * @return:	true if supported, false otherwise
> +	 */
> +	bool (*is_flash_command_supported)(struct udevice *bus,
> +					   const struct spi_flash_command *cmd);
> +
> +	/**
> +	 * Execute a SPI flash command
> +	 *
> +	 * @bus:	The SPI bus
> +	 * @cmd:	The SPI flash command to execute.
> +	 * @return 0 if OK, -ve on error
> +	 */
> +	int (*exec_flash_command)(struct udevice *bus,
> +				  const struct spi_flash_command *cmd);
>   };
>   
>   struct dm_spi_emul_ops {
> @@ -651,6 +801,24 @@ void dm_spi_release_bus(struct udevice *dev);
>   int dm_spi_xfer(struct udevice *dev, unsigned int bitlen,
>   		const void *dout, void *din, unsigned long flags);
>   
> +/**
> + * Check whether the given SPI flash command is supported.
> + *
> + * @dev:	The SPI slave device
> + * @cmd:	The SPI flash command
> + */
> +bool dm_spi_is_flash_command_supported(struct udevice *dev,
> +				       const struct spi_flash_command *cmd);
> +
> +/**
> + * Execute the given SPI flash command.
> + *
> + * @dev:	The SPI slave device
> + * @cmd:	The SPI flash command
> + */
> +int dm_spi_exec_flash_command(struct udevice *dev,
> +			      const struct spi_flash_command *cmd);
> +
>   /* Access the operations for a SPI device */
>   #define spi_get_ops(dev)	((struct dm_spi_ops *)(dev)->driver->ops)
>   #define spi_emul_get_ops(dev)	((struct dm_spi_emul_ops *)(dev)->driver->ops)

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 2/8] sf: describe all SPI flash commands with 'struct spi_flash_command'
  2017-05-19 14:59 ` [U-Boot] [PATCH 2/8] sf: describe all SPI flash commands with 'struct spi_flash_command' Cyrille Pitchen
@ 2017-05-22  1:18   ` Yang, Wenyou
  0 siblings, 0 replies; 17+ messages in thread
From: Yang, Wenyou @ 2017-05-22  1:18 UTC (permalink / raw)
  To: u-boot



On 2017/5/19 22:59, Cyrille Pitchen wrote:
> Now that the SPI sub-system API has been extended with
> 'struct spi_flash_command' and spi_is_flash_command_supported() /
> spi_exec_flash_command() functions, we update the SPI FLASH sub-system to
> use this new API.
>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

Acked-by Wenyou Yang <wenyou.yang@atmel.com>


Best Regards,
Wenyou Yang
> ---
>   drivers/mtd/spi/sf.c           |  78 +++++++++++++----
>   drivers/mtd/spi/sf_dataflash.c | 119 +++++++++++++-------------
>   drivers/mtd/spi/sf_internal.h  |  24 +++---
>   drivers/mtd/spi/spi_flash.c    | 184 +++++++++++++++++++++++------------------
>   4 files changed, 236 insertions(+), 169 deletions(-)
>
> diff --git a/drivers/mtd/spi/sf.c b/drivers/mtd/spi/sf.c
> index d5e175ca0000..6178b0aa9824 100644
> --- a/drivers/mtd/spi/sf.c
> +++ b/drivers/mtd/spi/sf.c
> @@ -9,46 +9,88 @@
>   
>   #include <common.h>
>   #include <spi.h>
> +#include <spi_flash.h>
>   
> -static int spi_flash_read_write(struct spi_slave *spi,
> -				const u8 *cmd, size_t cmd_len,
> -				const u8 *data_out, u8 *data_in,
> -				size_t data_len)
> +#include "sf_internal.h"
> +
> +static void spi_flash_addr(u32 addr, u8 addr_len, u8 *cmd_buf)
>   {
> +	u8 i;
> +
> +	for (i = 0; i < addr_len; i++)
> +		cmd_buf[i] = addr >> ((addr_len - 1 - i) * 8);
> +}
> +
> +static u8 spi_compute_num_dummy_bytes(enum spi_flash_protocol proto,
> +				      u8 num_dummy_clock_cycles)
> +{
> +	int shift = fls(spi_flash_protocol_get_addr_nbits(proto)) - 1;
> +
> +	if (shift < 0)
> +		shift = 0;
> +	return (num_dummy_clock_cycles << shift) >> 3;
> +}
> +
> +static int spi_flash_exec(struct spi_flash *flash,
> +			  const struct spi_flash_command *cmd)
> +{
> +	struct spi_slave *spi = flash->spi;
> +	u8 cmd_buf[SPI_FLASH_CMD_LEN];
> +	size_t cmd_len, num_dummy_bytes;
>   	unsigned long flags = SPI_XFER_BEGIN;
>   	int ret;
>   
> -	if (data_len == 0)
> +	if (spi_is_flash_command_supported(spi, cmd))
> +		return spi_exec_flash_command(spi, cmd);
> +
> +	if (cmd->data_len == 0)
>   		flags |= SPI_XFER_END;
>   
> -	ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags);
> +	cmd_buf[0] = cmd->inst;
> +	spi_flash_addr(cmd->addr, cmd->addr_len, cmd_buf + 1);
> +	cmd_len = 1 + cmd->addr_len;
> +
> +	num_dummy_bytes = spi_compute_num_dummy_bytes(cmd->proto,
> +						      cmd->num_mode_cycles +
> +						      cmd->num_wait_states);
> +	memset(cmd_buf + cmd_len, 0xff, num_dummy_bytes);
> +	cmd_len += num_dummy_bytes;
> +
> +	ret = spi_xfer(spi, cmd_len * 8, cmd_buf, NULL, flags);
>   	if (ret) {
>   		debug("SF: Failed to send command (%zu bytes): %d\n",
>   		      cmd_len, ret);
> -	} else if (data_len != 0) {
> -		ret = spi_xfer(spi, data_len * 8, data_out, data_in,
> -					SPI_XFER_END);
> +	} else if (cmd->data_len != 0) {
> +		ret = spi_xfer(spi, cmd->data_len * 8,
> +			       cmd->tx_data, cmd->rx_data,
> +			       SPI_XFER_END);
>   		if (ret)
>   			debug("SF: Failed to transfer %zu bytes of data: %d\n",
> -			      data_len, ret);
> +			      cmd->data_len, ret);
>   	}
>   
>   	return ret;
>   }
>   
> -int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
> -		size_t cmd_len, void *data, size_t data_len)
> +int spi_flash_cmd_read(struct spi_flash *flash,
> +		       const struct spi_flash_command *cmd)
>   {
> -	return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len);
> +	return spi_flash_exec(flash, cmd);
>   }
>   
> -int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len)
> +int spi_flash_cmd(struct spi_flash *flash, u8 instr, void *response, size_t len)
>   {
> -	return spi_flash_cmd_read(spi, &cmd, 1, response, len);
> +	struct spi_flash_command cmd;
> +	u8 flags = (response && len) ? SPI_FCMD_READ_REG : SPI_FCMD_WRITE_REG;
> +
> +	spi_flash_command_init(&cmd, instr, 0, flags);
> +	cmd.data_len = len;
> +	cmd.rx_data = response;
> +	return spi_flash_exec(flash, &cmd);
>   }
>   
> -int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
> -		const void *data, size_t data_len)
> +int spi_flash_cmd_write(struct spi_flash *flash,
> +			const struct spi_flash_command *cmd)
>   {
> -	return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len);
> +	return spi_flash_exec(flash, cmd);
>   }
> diff --git a/drivers/mtd/spi/sf_dataflash.c b/drivers/mtd/spi/sf_dataflash.c
> index bcddfa07556b..b2166ad4e5ff 100644
> --- a/drivers/mtd/spi/sf_dataflash.c
> +++ b/drivers/mtd/spi/sf_dataflash.c
> @@ -73,7 +73,7 @@ struct dataflash {
>   };
>   
>   /* Return the status of the DataFlash device */
> -static inline int dataflash_status(struct spi_slave *spi)
> +static inline int dataflash_status(struct spi_flash *spi_flash)
>   {
>   	int ret;
>   	u8 status;
> @@ -81,7 +81,7 @@ static inline int dataflash_status(struct spi_slave *spi)
>   	 * NOTE:  at45db321c over 25 MHz wants to write
>   	 * a dummy byte after the opcode...
>   	 */
> -	ret = spi_flash_cmd(spi, OP_READ_STATUS, &status, 1);
> +	ret = spi_flash_cmd(spi_flash, OP_READ_STATUS, &status, 1);
>   	return ret ? -EIO : status;
>   }
>   
> @@ -90,7 +90,7 @@ static inline int dataflash_status(struct spi_slave *spi)
>    * This usually takes 5-20 msec or so; more for sector erase.
>    * ready: return > 0
>    */
> -static int dataflash_waitready(struct spi_slave *spi)
> +static int dataflash_waitready(struct spi_flash *spi_flash)
>   {
>   	int status;
>   	int timeout = 2 * CONFIG_SYS_HZ;
> @@ -98,7 +98,7 @@ static int dataflash_waitready(struct spi_slave *spi)
>   
>   	timebase = get_timer(0);
>   	do {
> -		status = dataflash_status(spi);
> +		status = dataflash_status(spi_flash);
>   		if (status < 0)
>   			status = 0;
>   
> @@ -114,11 +114,11 @@ static int dataflash_waitready(struct spi_slave *spi)
>   /* Erase pages of flash */
>   static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len)
>   {
> +	struct spi_flash_command	cmd;
>   	struct dataflash	*dataflash;
>   	struct spi_flash	*spi_flash;
>   	struct spi_slave	*spi;
>   	unsigned		blocksize;
> -	uint8_t			*command;
>   	uint32_t		rem;
>   	int			status;
>   
> @@ -128,9 +128,6 @@ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len)
>   
>   	blocksize = spi_flash->page_size << 3;
>   
> -	memset(dataflash->command, 0 , sizeof(dataflash->command));
> -	command = dataflash->command;
> -
>   	debug("%s: erase addr=0x%x len 0x%x\n", dev->name, offset, len);
>   
>   	div_u64_rem(len, spi_flash->page_size, &rem);
> @@ -146,6 +143,8 @@ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len)
>   		return status;
>   	}
>   
> +	spi_flash_command_init(&cmd, OP_ERASE_BLOCK, SPI_FLASH_3B_ADDR_LEN,
> +			       SPI_FCMD_ERASE);
>   	while (len > 0) {
>   		unsigned int	pageaddr;
>   		int		do_block;
> @@ -157,23 +156,24 @@ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len)
>   		do_block = (pageaddr & 0x7) == 0 && len >= blocksize;
>   		pageaddr = pageaddr << dataflash->page_offset;
>   
> -		command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
> -		command[1] = (uint8_t)(pageaddr >> 16);
> -		command[2] = (uint8_t)(pageaddr >> 8);
> -		command[3] = 0;
> +		cmd.inst = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
> +		cmd.addr = pageaddr & 0x00FFFF00;
>   
>   		debug("%s ERASE %s: (%x) %x %x %x [%d]\n",
>   		      dev->name, do_block ? "block" : "page",
> -		      command[0], command[1], command[2], command[3],
> +		      cmd.inst,
> +		      (cmd.addr >> 16) & 0xff,
> +		      (cmd.addr >>  8) & 0xff,
> +		      (cmd.addr >>  0) & 0xff,
>   		      pageaddr);
>   
> -		status = spi_flash_cmd_write(spi, command, 4, NULL, 0);
> +		status = spi_flash_cmd_write(spi_flash, &cmd);
>   		if (status < 0) {
>   			debug("%s: erase send command error!\n", dev->name);
>   			return -EIO;
>   		}
>   
> -		status = dataflash_waitready(spi);
> +		status = dataflash_waitready(spi_flash);
>   		if (status < 0) {
>   			debug("%s: erase waitready error!\n", dev->name);
>   			return status;
> @@ -202,23 +202,18 @@ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len)
>   static int spi_dataflash_read(struct udevice *dev, u32 offset, size_t len,
>   			      void *buf)
>   {
> +	struct spi_flash_command	cmd;
>   	struct dataflash	*dataflash;
>   	struct spi_flash	*spi_flash;
>   	struct spi_slave	*spi;
>   	unsigned int		addr;
> -	uint8_t			*command;
>   	int			status;
>   
>   	dataflash = dev_get_priv(dev);
>   	spi_flash = dev_get_uclass_priv(dev);
>   	spi = spi_flash->spi;
>   
> -	memset(dataflash->command, 0 , sizeof(dataflash->command));
> -	command = dataflash->command;
> -
> -	debug("%s: erase addr=0x%x len 0x%x\n", dev->name, offset, len);
> -	debug("READ: (%x) %x %x %x\n",
> -	      command[0], command[1], command[2], command[3]);
> +	debug("%s: read addr=0x%x len 0x%x\n", dev->name, offset, len);
>   
>   	/* Calculate flash page/byte address */
>   	addr = (((unsigned)offset / spi_flash->page_size)
> @@ -236,13 +231,15 @@ static int spi_dataflash_read(struct udevice *dev, u32 offset, size_t len,
>   	 * the peak rate available.  Some chips support commands with
>   	 * fewer "don't care" bytes.  Both buffers stay unchanged.
>   	 */
> -	command[0] = OP_READ_CONTINUOUS;
> -	command[1] = (uint8_t)(addr >> 16);
> -	command[2] = (uint8_t)(addr >> 8);
> -	command[3] = (uint8_t)(addr >> 0);
> +	spi_flash_command_init(&cmd, OP_READ_CONTINUOUS, SPI_FLASH_3B_ADDR_LEN,
> +			       SPI_FCMD_READ);
> +	cmd.addr = addr;
> +	cmd.num_wait_states = 4 * 8; /* 4 "don't care" bytes */
> +	cmd.data_len = len;
> +	cmd.rx_data = buf;
>   
>   	/* plus 4 "don't care" bytes, command len: 4 + 4 "don't care" bytes */
> -	status = spi_flash_cmd_read(spi, command, 8, buf, len);
> +	status = spi_flash_cmd_read(spi_flash, &cmd);
>   
>   	spi_release_bus(spi);
>   
> @@ -258,10 +255,10 @@ static int spi_dataflash_read(struct udevice *dev, u32 offset, size_t len,
>   int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
>   			const void *buf)
>   {
> +	struct spi_flash_command	cmd;
>   	struct dataflash	*dataflash;
>   	struct spi_flash	*spi_flash;
>   	struct spi_slave	*spi;
> -	uint8_t			*command;
>   	unsigned int		pageaddr, addr, to, writelen;
>   	size_t			remaining = len;
>   	u_char			*writebuf = (u_char *)buf;
> @@ -271,9 +268,6 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
>   	spi_flash = dev_get_uclass_priv(dev);
>   	spi = spi_flash->spi;
>   
> -	memset(dataflash->command, 0 , sizeof(dataflash->command));
> -	command = dataflash->command;
> -
>   	debug("%s: write 0x%x..0x%x\n", dev->name, offset, (offset + len));
>   
>   	pageaddr = ((unsigned)offset / spi_flash->page_size);
> @@ -289,6 +283,8 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
>   		return status;
>   	}
>   
> +	spi_flash_command_init(&cmd, OP_TRANSFER_BUF1, SPI_FLASH_3B_ADDR_LEN,
> +			       SPI_FCMD_WRITE);
>   	while (remaining > 0) {
>   		debug("write @ %d:%d len=%d\n", pageaddr, to, writelen);
>   
> @@ -313,22 +309,25 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
>   
>   		/* (1) Maybe transfer partial page to Buffer1 */
>   		if (writelen != spi_flash->page_size) {
> -			command[0] = OP_TRANSFER_BUF1;
> -			command[1] = (addr & 0x00FF0000) >> 16;
> -			command[2] = (addr & 0x0000FF00) >> 8;
> -			command[3] = 0;
> +			cmd.inst = OP_TRANSFER_BUF1;
> +			cmd.addr = (addr & 0x00FFFF00);
> +			cmd.data_len = 0;
> +			cmd.tx_data = NULL;
>   
>   			debug("TRANSFER: (%x) %x %x %x\n",
> -			      command[0], command[1], command[2], command[3]);
> +			      cmd.inst,
> +			      (cmd.addr >> 16) & 0xff,
> +			      (cmd.addr >>  8) & 0xff,
> +			      (cmd.addr >>  0) & 0xff);
>   
> -			status = spi_flash_cmd_write(spi, command, 4, NULL, 0);
> +			status = spi_flash_cmd_write(spi_flash, &cmd);
>   			if (status < 0) {
>   				debug("%s: write(<pagesize) command error!\n",
>   				      dev->name);
>   				return -EIO;
>   			}
>   
> -			status = dataflash_waitready(spi);
> +			status = dataflash_waitready(spi_flash);
>   			if (status < 0) {
>   				debug("%s: write(<pagesize) waitready error!\n",
>   				      dev->name);
> @@ -338,22 +337,24 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
>   
>   		/* (2) Program full page via Buffer1 */
>   		addr += to;
> -		command[0] = OP_PROGRAM_VIA_BUF1;
> -		command[1] = (addr & 0x00FF0000) >> 16;
> -		command[2] = (addr & 0x0000FF00) >> 8;
> -		command[3] = (addr & 0x000000FF);
> +		cmd.inst = OP_PROGRAM_VIA_BUF1;
> +		cmd.addr = addr;
> +		cmd.data_len = writelen;
> +		cmd.tx_data = writebuf;
>   
>   		debug("PROGRAM: (%x) %x %x %x\n",
> -		      command[0], command[1], command[2], command[3]);
> +		      cmd.inst,
> +		      (cmd.addr >> 16) & 0xff,
> +		      (cmd.addr >>  8) & 0xff,
> +		      (cmd.addr >>  0) & 0xff);
>   
> -		status = spi_flash_cmd_write(spi, command,
> -					     4, writebuf, writelen);
> +		status = spi_flash_cmd_write(spi_flash, &cmd);
>   		if (status < 0) {
>   			debug("%s: write send command error!\n", dev->name);
>   			return -EIO;
>   		}
>   
> -		status = dataflash_waitready(spi);
> +		status = dataflash_waitready(spi_flash);
>   		if (status < 0) {
>   			debug("%s: write waitready error!\n", dev->name);
>   			return status;
> @@ -362,16 +363,18 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
>   #ifdef CONFIG_SPI_DATAFLASH_WRITE_VERIFY
>   		/* (3) Compare to Buffer1 */
>   		addr = pageaddr << dataflash->page_offset;
> -		command[0] = OP_COMPARE_BUF1;
> -		command[1] = (addr & 0x00FF0000) >> 16;
> -		command[2] = (addr & 0x0000FF00) >> 8;
> -		command[3] = 0;
> +		cmd.inst = OP_COMPARE_BUF1;
> +		cmd.addr = addr & 0x00FFFF00;
> +		cmd.data_len = writelen;
> +		cmd.tx_data = writebuf;
>   
>   		debug("COMPARE: (%x) %x %x %x\n",
> -		      command[0], command[1], command[2], command[3]);
> +		      cmd.inst,
> +		      (cmd.addr >> 16) & 0xff,
> +		      (cmd.addr >>  8) & 0xff,
> +		      (cmd.addr >>  0) & 0xff);
>   
> -		status = spi_flash_cmd_write(spi, command,
> -					     4, writebuf, writelen);
> +		status = spi_flash_cmd_write(spi, &cmd);
>   		if (status < 0) {
>   			debug("%s: write(compare) send command error!\n",
>   			      dev->name);
> @@ -496,7 +499,7 @@ static struct flash_info dataflash_data[] = {
>   	{ "at45db642d",  0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS},
>   };
>   
> -static struct flash_info *jedec_probe(struct spi_slave *spi)
> +static struct flash_info *jedec_probe(struct spi_flash *spi_flash)
>   {
>   	int			tmp;
>   	uint8_t			id[5];
> @@ -513,7 +516,7 @@ static struct flash_info *jedec_probe(struct spi_slave *spi)
>   	 * That's not an error; only rev C and newer chips handle it, and
>   	 * only Atmel sells these chips.
>   	 */
> -	tmp = spi_flash_cmd(spi, CMD_READ_ID, id, sizeof(id));
> +	tmp = spi_flash_cmd(spi_flash, CMD_READ_ID, id, sizeof(id));
>   	if (tmp < 0) {
>   		printf("dataflash: error %d reading JEDEC ID\n", tmp);
>   		return ERR_PTR(tmp);
> @@ -532,7 +535,7 @@ static struct flash_info *jedec_probe(struct spi_slave *spi)
>   			tmp++, info++) {
>   		if (info->jedec_id == jedec) {
>   			if (info->flags & SUP_POW2PS) {
> -				status = dataflash_status(spi);
> +				status = dataflash_status(spi_flash);
>   				if (status < 0) {
>   					debug("dataflash: status error %d\n",
>   					      status);
> @@ -596,7 +599,7 @@ static int spi_dataflash_probe(struct udevice *dev)
>   	 * Both support the security register, though with different
>   	 * write procedures.
>   	 */
> -	info = jedec_probe(spi);
> +	info = jedec_probe(spi_flash);
>   	if (IS_ERR(info))
>   		goto err_jedec_probe;
>   	if (info != NULL) {
> @@ -611,7 +614,7 @@ static int spi_dataflash_probe(struct udevice *dev)
>   	* Older chips support only legacy commands, identifing
>   	* capacity using bits in the status byte.
>   	*/
> -	status = dataflash_status(spi);
> +	status = dataflash_status(spi_flash);
>   	if (status <= 0 || status == 0xff) {
>   		printf("dataflash: read status error %d\n", status);
>   		if (status == 0 || status == 0xff)
> diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
> index 839cdbe1b0f1..5c551089d673 100644
> --- a/drivers/mtd/spi/sf_internal.h
> +++ b/drivers/mtd/spi/sf_internal.h
> @@ -27,7 +27,7 @@ enum spi_nor_option_flags {
>   };
>   
>   #define SPI_FLASH_3B_ADDR_LEN		3
> -#define SPI_FLASH_CMD_LEN		(1 + SPI_FLASH_3B_ADDR_LEN)
> +#define SPI_FLASH_CMD_LEN		(1 + SPI_FLASH_3B_ADDR_LEN + 16)
>   #define SPI_FLASH_16MB_BOUN		0x1000000
>   
>   /* CFI Manufacture ID's */
> @@ -137,21 +137,21 @@ struct spi_flash_info {
>   extern const struct spi_flash_info spi_flash_ids[];
>   
>   /* Send a single-byte command to the device and read the response */
> -int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len);
> +int spi_flash_cmd(struct spi_flash *flash, u8 instr, void *response, size_t len);
>   
>   /*
>    * Send a multi-byte command to the device and read the response. Used
>    * for flash array reads, etc.
>    */
> -int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
> -		size_t cmd_len, void *data, size_t data_len);
> +int spi_flash_cmd_read(struct spi_flash *flash,
> +		       const struct spi_flash_command *cmd);
>   
>   /*
>    * Send a multi-byte command to the device followed by (optional)
>    * data. Used for programming the flash array, etc.
>    */
> -int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
> -		const void *data, size_t data_len);
> +int spi_flash_cmd_write(struct spi_flash *flash,
> +			const struct spi_flash_command *cmd);
>   
>   
>   /* Flash erase(sectors) operation, support all possible erase commands */
> @@ -169,13 +169,13 @@ int stm_is_locked(struct spi_flash *flash, u32 ofs, size_t len);
>   /* Enable writing on the SPI flash */
>   static inline int spi_flash_cmd_write_enable(struct spi_flash *flash)
>   {
> -	return spi_flash_cmd(flash->spi, CMD_WRITE_ENABLE, NULL, 0);
> +	return spi_flash_cmd(flash, CMD_WRITE_ENABLE, NULL, 0);
>   }
>   
>   /* Disable writing on the SPI flash */
>   static inline int spi_flash_cmd_write_disable(struct spi_flash *flash)
>   {
> -	return spi_flash_cmd(flash->spi, CMD_WRITE_DISABLE, NULL, 0);
> +	return spi_flash_cmd(flash, CMD_WRITE_DISABLE, NULL, 0);
>   }
>   
>   /*
> @@ -186,8 +186,8 @@ static inline int spi_flash_cmd_write_disable(struct spi_flash *flash)
>    * - spi_flash_wait_till_ready
>    * - SPI release
>    */
> -int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
> -		size_t cmd_len, const void *buf, size_t buf_len);
> +int spi_flash_write_common(struct spi_flash *flash,
> +			   const struct spi_flash_command *cmd);
>   
>   /*
>    * Flash write operation, support all possible write commands.
> @@ -201,8 +201,8 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
>    * Same as spi_flash_cmd_read() except it also claims/releases the SPI
>    * bus. Used as common part of the ->read() operation.
>    */
> -int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
> -		size_t cmd_len, void *data, size_t data_len);
> +int spi_flash_read_common(struct spi_flash *flash,
> +			  const struct spi_flash_command *cmd);
>   
>   /* Flash read operation, support all possible read commands */
>   int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
> diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
> index ab7910bc1450..d0634e5b28c7 100644
> --- a/drivers/mtd/spi/spi_flash.c
> +++ b/drivers/mtd/spi/spi_flash.c
> @@ -22,21 +22,15 @@
>   
>   DECLARE_GLOBAL_DATA_PTR;
>   
> -static void spi_flash_addr(u32 addr, u8 *cmd)
> -{
> -	/* cmd[0] is actual command */
> -	cmd[1] = addr >> 16;
> -	cmd[2] = addr >> 8;
> -	cmd[3] = addr >> 0;
> -}
> -
>   static int read_sr(struct spi_flash *flash, u8 *rs)
>   {
> +	struct spi_flash_command cmd;
>   	int ret;
> -	u8 cmd;
>   
> -	cmd = CMD_READ_STATUS;
> -	ret = spi_flash_read_common(flash, &cmd, 1, rs, 1);
> +	spi_flash_command_init(&cmd, CMD_READ_STATUS, 0, SPI_FCMD_READ_REG);
> +	cmd.data_len = 1;
> +	cmd.rx_data = rs;
> +	ret = spi_flash_read_common(flash, &cmd);
>   	if (ret < 0) {
>   		debug("SF: fail to read status register\n");
>   		return ret;
> @@ -47,10 +41,13 @@ static int read_sr(struct spi_flash *flash, u8 *rs)
>   
>   static int read_fsr(struct spi_flash *flash, u8 *fsr)
>   {
> +	struct spi_flash_command cmd;
>   	int ret;
> -	const u8 cmd = CMD_FLAG_STATUS;
>   
> -	ret = spi_flash_read_common(flash, &cmd, 1, fsr, 1);
> +	spi_flash_command_init(&cmd, CMD_FLAG_STATUS, 0, SPI_FCMD_READ_REG);
> +	cmd.data_len = 1;
> +	cmd.rx_data = fsr;
> +	ret = spi_flash_read_common(flash, &cmd);
>   	if (ret < 0) {
>   		debug("SF: fail to read flag status register\n");
>   		return ret;
> @@ -61,11 +58,13 @@ static int read_fsr(struct spi_flash *flash, u8 *fsr)
>   
>   static int write_sr(struct spi_flash *flash, u8 ws)
>   {
> -	u8 cmd;
> +	struct spi_flash_command cmd;
>   	int ret;
>   
> -	cmd = CMD_WRITE_STATUS;
> -	ret = spi_flash_write_common(flash, &cmd, 1, &ws, 1);
> +	spi_flash_command_init(&cmd, CMD_WRITE_STATUS, 0, SPI_FCMD_WRITE_REG);
> +	cmd.data_len = 1;
> +	cmd.tx_data = &ws;
> +	ret = spi_flash_write_common(flash, &cmd);
>   	if (ret < 0) {
>   		debug("SF: fail to write status register\n");
>   		return ret;
> @@ -77,11 +76,13 @@ static int write_sr(struct spi_flash *flash, u8 ws)
>   #if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
>   static int read_cr(struct spi_flash *flash, u8 *rc)
>   {
> +	struct spi_flash_command cmd;
>   	int ret;
> -	u8 cmd;
>   
> -	cmd = CMD_READ_CONFIG;
> -	ret = spi_flash_read_common(flash, &cmd, 1, rc, 1);
> +	spi_flash_command_init(&cmd, CMD_READ_CONFIG, 0, SPI_FCMD_READ_REG);
> +	cmd.data_len = 1;
> +	cmd.rx_data = rc;
> +	ret = spi_flash_read_common(flash, &cmd);
>   	if (ret < 0) {
>   		debug("SF: fail to read config register\n");
>   		return ret;
> @@ -92,17 +93,19 @@ static int read_cr(struct spi_flash *flash, u8 *rc)
>   
>   static int write_cr(struct spi_flash *flash, u8 wc)
>   {
> +	struct spi_flash_command cmd;
>   	u8 data[2];
> -	u8 cmd;
>   	int ret;
>   
>   	ret = read_sr(flash, &data[0]);
>   	if (ret < 0)
>   		return ret;
>   
> -	cmd = CMD_WRITE_STATUS;
> +	spi_flash_command_init(&cmd, CMD_WRITE_STATUS, 0, SPI_FCMD_WRITE_REG);
> +	cmd.data_len = 2;
> +	cmd.tx_data = data;
>   	data[1] = wc;
> -	ret = spi_flash_write_common(flash, &cmd, 1, &data, 2);
> +	ret = spi_flash_write_common(flash, &cmd);
>   	if (ret) {
>   		debug("SF: fail to write config register\n");
>   		return ret;
> @@ -115,15 +118,19 @@ static int write_cr(struct spi_flash *flash, u8 wc)
>   #ifdef CONFIG_SPI_FLASH_BAR
>   static int write_bar(struct spi_flash *flash, u32 offset)
>   {
> -	u8 cmd, bank_sel;
> +	struct spi_flash_command cmd;
> +	u8 bank_sel;
>   	int ret;
>   
>   	bank_sel = offset / (SPI_FLASH_16MB_BOUN << flash->shift);
>   	if (bank_sel == flash->bank_curr)
>   		goto bar_end;
>   
> -	cmd = flash->bank_write_cmd;
> -	ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1);
> +	spi_flash_command_init(&cmd, flash->bank_write_cmd, 0,
> +			       SPI_FCMD_WRITE_REG);
> +	cmd.data_len = 1;
> +	cmd.tx_data = &bank_sel;
> +	ret = spi_flash_write_common(flash, &cmd);
>   	if (ret < 0) {
>   		debug("SF: fail to write bank register\n");
>   		return ret;
> @@ -136,6 +143,7 @@ bar_end:
>   
>   static int read_bar(struct spi_flash *flash, const struct spi_flash_info *info)
>   {
> +	struct spi_flash_command cmd;
>   	u8 curr_bank = 0;
>   	int ret;
>   
> @@ -152,8 +160,11 @@ static int read_bar(struct spi_flash *flash, const struct spi_flash_info *info)
>   		flash->bank_write_cmd = CMD_EXTNADDR_WREAR;
>   	}
>   
> -	ret = spi_flash_read_common(flash, &flash->bank_read_cmd, 1,
> -				    &curr_bank, 1);
> +	spi_flash_command_init(&cmd, flash->bank_read_cmd, 0,
> +			       SPI_FCMD_READ_REG);
> +	cmd.data_len = 1;
> +	cmd.rx_data = &curr_bank;
> +	ret = spi_flash_read_common(flash, &cmd);
>   	if (ret) {
>   		debug("SF: fail to read bank addr register\n");
>   		return ret;
> @@ -250,14 +261,14 @@ static int spi_flash_wait_till_ready(struct spi_flash *flash,
>   	return -ETIMEDOUT;
>   }
>   
> -int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
> -		size_t cmd_len, const void *buf, size_t buf_len)
> +int spi_flash_write_common(struct spi_flash *flash,
> +			   const struct spi_flash_command *cmd)
>   {
>   	struct spi_slave *spi = flash->spi;
>   	unsigned long timeout = SPI_FLASH_PROG_TIMEOUT;
>   	int ret;
>   
> -	if (buf == NULL)
> +	if (cmd->tx_data == NULL)
>   		timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT;
>   
>   	ret = spi_claim_bus(spi);
> @@ -272,7 +283,7 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
>   		return ret;
>   	}
>   
> -	ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len);
> +	ret = spi_flash_cmd_write(flash, cmd);
>   	if (ret < 0) {
>   		debug("SF: write cmd failed\n");
>   		return ret;
> @@ -293,8 +304,8 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
>   
>   int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
>   {
> +	struct spi_flash_command cmd;
>   	u32 erase_size, erase_addr;
> -	u8 cmd[SPI_FLASH_CMD_LEN];
>   	int ret = -1;
>   
>   	erase_size = flash->erase_size;
> @@ -311,7 +322,8 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
>   		}
>   	}
>   
> -	cmd[0] = flash->erase_cmd;
> +	spi_flash_command_init(&cmd, flash->erase_cmd, SPI_FLASH_3B_ADDR_LEN,
> +			       SPI_FCMD_ERASE);
>   	while (len) {
>   		erase_addr = offset;
>   
> @@ -324,12 +336,15 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
>   		if (ret < 0)
>   			return ret;
>   #endif
> -		spi_flash_addr(erase_addr, cmd);
> +		cmd.addr = erase_addr;
>   
> -		debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1],
> -		      cmd[2], cmd[3], erase_addr);
> +		debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd.inst,
> +		      (cmd.addr >> 16) & 0xff,
> +		      (cmd.addr >>  8) & 0xff,
> +		      (cmd.addr >>  0) & 0xff,
> +		      erase_addr);
>   
> -		ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0);
> +		ret = spi_flash_write_common(flash, &cmd);
>   		if (ret < 0) {
>   			debug("SF: erase failed\n");
>   			break;
> @@ -346,10 +361,10 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
>   		size_t len, const void *buf)
>   {
>   	struct spi_slave *spi = flash->spi;
> +	struct spi_flash_command cmd;
>   	unsigned long byte_addr, page_size;
>   	u32 write_addr;
>   	size_t chunk_len, actual;
> -	u8 cmd[SPI_FLASH_CMD_LEN];
>   	int ret = -1;
>   
>   	page_size = flash->page_size;
> @@ -362,7 +377,8 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
>   		}
>   	}
>   
> -	cmd[0] = flash->write_cmd;
> +	spi_flash_command_init(&cmd, flash->write_cmd, SPI_FLASH_3B_ADDR_LEN,
> +			       SPI_FCMD_WRITE);
>   	for (actual = 0; actual < len; actual += chunk_len) {
>   		write_addr = offset;
>   
> @@ -382,13 +398,18 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
>   			chunk_len = min(chunk_len,
>   					(size_t)spi->max_write_size);
>   
> -		spi_flash_addr(write_addr, cmd);
> +		cmd.addr = write_addr;
> +		cmd.data_len = chunk_len;
> +		cmd.tx_data = buf + actual;
>   
>   		debug("SF: 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);
> +		      buf + actual, cmd.inst,
> +		      (cmd.addr >> 16) & 0xff,
> +		      (cmd.addr >>  8) & 0xff,
> +		      (cmd.addr >>  0) & 0xff,
> +		      chunk_len);
>   
> -		ret = spi_flash_write_common(flash, cmd, sizeof(cmd),
> -					buf + actual, chunk_len);
> +		ret = spi_flash_write_common(flash, &cmd);
>   		if (ret < 0) {
>   			debug("SF: write failed\n");
>   			break;
> @@ -400,8 +421,8 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
>   	return ret;
>   }
>   
> -int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
> -		size_t cmd_len, void *data, size_t data_len)
> +int spi_flash_read_common(struct spi_flash *flash,
> +			  const struct spi_flash_command *cmd)
>   {
>   	struct spi_slave *spi = flash->spi;
>   	int ret;
> @@ -412,7 +433,7 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
>   		return ret;
>   	}
>   
> -	ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len);
> +	ret = spi_flash_cmd_read(flash, cmd);
>   	if (ret < 0) {
>   		debug("SF: read cmd failed\n");
>   		return ret;
> @@ -440,7 +461,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
>   		size_t len, void *data)
>   {
>   	struct spi_slave *spi = flash->spi;
> -	u8 *cmd, cmdsz;
> +	struct spi_flash_command cmd;
>   	u32 remain_len, read_len, read_addr;
>   	int bank_sel = 0;
>   	int ret = -1;
> @@ -459,14 +480,9 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
>   		return 0;
>   	}
>   
> -	cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte;
> -	cmd = calloc(1, cmdsz);
> -	if (!cmd) {
> -		debug("SF: Failed to allocate cmd\n");
> -		return -ENOMEM;
> -	}
> -
> -	cmd[0] = flash->read_cmd;
> +	spi_flash_command_init(&cmd, flash->read_cmd, SPI_FLASH_3B_ADDR_LEN,
> +			       SPI_FCMD_READ);
> +	cmd.num_wait_states = flash->dummy_byte * 8;
>   	while (len) {
>   		read_addr = offset;
>   
> @@ -487,9 +503,10 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
>   		else
>   			read_len = remain_len;
>   
> -		spi_flash_addr(read_addr, cmd);
> -
> -		ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len);
> +		cmd.addr = read_addr;
> +		cmd.data_len = read_len;
> +		cmd.rx_data = data;
> +		ret = spi_flash_read_common(flash, &cmd);
>   		if (ret < 0) {
>   			debug("SF: read failed\n");
>   			break;
> @@ -500,30 +517,33 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
>   		data += read_len;
>   	}
>   
> -	free(cmd);
>   	return ret;
>   }
>   
>   #ifdef CONFIG_SPI_FLASH_SST
>   static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf)
>   {
> -	struct spi_slave *spi = flash->spi;
> +	struct spi_flash_command cmd;
>   	int ret;
> -	u8 cmd[4] = {
> -		CMD_SST_BP,
> -		offset >> 16,
> -		offset >> 8,
> -		offset,
> -	};
> +	u8 sr = 0xFFu;
> +
> +	spi_flash_command_init(&cmd, CMD_SST_BP, SPI_FLASH_3B_ADDR_LEN,
> +			       SPI_FCMD_WRITE);
> +	cmd.addr = offset;
> +	cmd.data_len = 1;
> +	cmd.tx_data = buf;
>   
> +#ifdef DEBUG
> +	read_sr(flash, &sr);
> +#endif
>   	debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
> -	      spi_w8r8(spi, CMD_READ_STATUS), buf, cmd[0], offset);
> +	      sr, buf, cmd.inst, offset);
>   
>   	ret = spi_flash_cmd_write_enable(flash);
>   	if (ret)
>   		return ret;
>   
> -	ret = spi_flash_cmd_write(spi, cmd, sizeof(cmd), buf, 1);
> +	ret = spi_flash_cmd_write(flash, &cmd);
>   	if (ret)
>   		return ret;
>   
> @@ -534,9 +554,10 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
>   		const void *buf)
>   {
>   	struct spi_slave *spi = flash->spi;
> -	size_t actual, cmd_len;
> +	struct spi_flash_command cmd;
> +	size_t actual;
>   	int ret;
> -	u8 cmd[4];
> +	u8 sr = 0xFFu;
>   
>   	ret = spi_claim_bus(spi);
>   	if (ret) {
> @@ -557,19 +578,20 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
>   	if (ret)
>   		goto done;
>   
> -	cmd_len = 4;
> -	cmd[0] = CMD_SST_AAI_WP;
> -	cmd[1] = offset >> 16;
> -	cmd[2] = offset >> 8;
> -	cmd[3] = offset;
> +	spi_flash_command_init(&cmd, CMD_SST_AAI_WP, SPI_FLASH_3B_ADDR_LEN,
> +			       SPI_FCMD_WRITE);
> +	cmd.addr = offset;
> +	cmd.data_len = 2;
>   
>   	for (; actual < len - 1; actual += 2) {
> +#ifdef DEBUG
> +		read_sr(flash, &sr);
> +#endif
>   		debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
> -		      spi_w8r8(spi, CMD_READ_STATUS), buf + actual,
> -		      cmd[0], offset);
> +		      sr, buf + actual, cmd.inst, offset);
>   
> -		ret = spi_flash_cmd_write(spi, cmd, cmd_len,
> -					buf + actual, 2);
> +		cmd.tx_data = buf + actual;
> +		ret = spi_flash_cmd_write(flash, &cmd);
>   		if (ret) {
>   			debug("SF: sst word program failed\n");
>   			break;
> @@ -579,7 +601,7 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
>   		if (ret)
>   			break;
>   
> -		cmd_len = 1;
> +		cmd.addr_len = 0;
>   		offset += 2;
>   	}
>   
> @@ -869,7 +891,7 @@ static const struct spi_flash_info *spi_flash_read_id(struct spi_flash *flash)
>   	u8				id[SPI_FLASH_MAX_ID_LEN];
>   	const struct spi_flash_info	*info;
>   
> -	tmp = spi_flash_cmd(flash->spi, CMD_READ_ID, id, SPI_FLASH_MAX_ID_LEN);
> +	tmp = spi_flash_cmd(flash, CMD_READ_ID, id, SPI_FLASH_MAX_ID_LEN);
>   	if (tmp < 0) {
>   		printf("SF: error %d reading JEDEC ID\n", tmp);
>   		return ERR_PTR(tmp);

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 3/8] sf: select the relevant SPI flash protocol for read and write commands
  2017-05-19 14:59 ` [U-Boot] [PATCH 3/8] sf: select the relevant SPI flash protocol for read and write commands Cyrille Pitchen
@ 2017-05-22  1:18   ` Yang, Wenyou
  0 siblings, 0 replies; 17+ messages in thread
From: Yang, Wenyou @ 2017-05-22  1:18 UTC (permalink / raw)
  To: u-boot



On 2017/5/19 22:59, Cyrille Pitchen wrote:
> SPI controller drivers should not check the instruction op code byte to
> guess which SPI x-y-z protocol is to be used for Fast Read or Page Program
> operations.
>
> Indeed, the op code values are not so reliable. For instance, the 32h op
> code is generally used for Page Program 1-1-4 operations. However
> Microchip SST26 memories use this 32h op code for their Page Program 1-4-4
> operations. There are many other examples of those SPI flash manufacturer
> quirks.
>
> Instead, the SPI FLASH sub-system now fills the 'proto' member
> of 'struct spi_flash_command' with flash->read_proto for Fast Read
> operations and flash->write_proto for Page Program operations.
>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

Acked-by Wenyou Yang <wenyou.yang@atmel.com>


Best Regards,
Wenyou Yang

> ---
>   drivers/mtd/spi/spi_flash.c | 24 ++++++++++++++++--------
>   include/spi_flash.h         |  4 ++++
>   2 files changed, 20 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
> index d0634e5b28c7..7e35fb9f4802 100644
> --- a/drivers/mtd/spi/spi_flash.c
> +++ b/drivers/mtd/spi/spi_flash.c
> @@ -379,6 +379,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
>   
>   	spi_flash_command_init(&cmd, flash->write_cmd, SPI_FLASH_3B_ADDR_LEN,
>   			       SPI_FCMD_WRITE);
> +	cmd.proto = flash->write_proto;
>   	for (actual = 0; actual < len; actual += chunk_len) {
>   		write_addr = offset;
>   
> @@ -482,6 +483,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
>   
>   	spi_flash_command_init(&cmd, flash->read_cmd, SPI_FLASH_3B_ADDR_LEN,
>   			       SPI_FCMD_READ);
> +	cmd.proto = flash->read_proto;
>   	cmd.num_wait_states = flash->dummy_byte * 8;
>   	while (len) {
>   		read_addr = offset;
> @@ -1045,24 +1047,30 @@ int spi_flash_scan(struct spi_flash *flash)
>   
>   	/* Look for read commands */
>   	flash->read_cmd = CMD_READ_ARRAY_FAST;
> -	if (spi->mode & SPI_RX_SLOW)
> +	flash->read_proto = SPI_FPROTO_1_1_1;
> +	if (spi->mode & SPI_RX_SLOW) {
>   		flash->read_cmd = CMD_READ_ARRAY_SLOW;
> -	else if (spi->mode & SPI_RX_QUAD && info->flags & RD_QUAD)
> +	} else if (spi->mode & SPI_RX_QUAD && info->flags & RD_QUAD) {
>   		flash->read_cmd = CMD_READ_QUAD_OUTPUT_FAST;
> -	else if (spi->mode & SPI_RX_DUAL && info->flags & RD_DUAL)
> +		flash->read_proto = SPI_FPROTO_1_1_4;
> +	} else if (spi->mode & SPI_RX_DUAL && info->flags & RD_DUAL) {
>   		flash->read_cmd = CMD_READ_DUAL_OUTPUT_FAST;
> +		flash->read_proto = SPI_FPROTO_1_1_2;
> +	}
>   
>   	/* Look for write commands */
> -	if (info->flags & WR_QPP && spi->mode & SPI_TX_QUAD)
> +	if (info->flags & WR_QPP && spi->mode & SPI_TX_QUAD) {
>   		flash->write_cmd = CMD_QUAD_PAGE_PROGRAM;
> -	else
> +		flash->write_proto = SPI_FPROTO_1_1_4;
> +	} else {
>   		/* Go for default supported write cmd */
>   		flash->write_cmd = CMD_PAGE_PROGRAM;
> +		flash->write_proto = SPI_FPROTO_1_1_1;
> +	}
>   
>   	/* Set the quad enable bit - only for quad commands */
> -	if ((flash->read_cmd == CMD_READ_QUAD_OUTPUT_FAST) ||
> -	    (flash->read_cmd == CMD_READ_QUAD_IO_FAST) ||
> -	    (flash->write_cmd == CMD_QUAD_PAGE_PROGRAM)) {
> +	if (spi_flash_protocol_get_data_nbits(flash->read_proto) == 4 ||
> +	    spi_flash_protocol_get_data_nbits(flash->write_proto) == 4) {
>   		ret = set_quad_mode(flash, info);
>   		if (ret) {
>   			debug("SF: Fail to set QEB for %02x\n",
> diff --git a/include/spi_flash.h b/include/spi_flash.h
> index be2fe3f84cb9..ac2b37f0202f 100644
> --- a/include/spi_flash.h
> +++ b/include/spi_flash.h
> @@ -48,6 +48,8 @@ struct spi_slave;
>    * @read_cmd:		Read cmd - Array Fast, Extn read and quad read.
>    * @write_cmd:		Write cmd - page and quad program.
>    * @dummy_byte:		Dummy cycles for read operation.
> + * @read_proto:		SPI x-y-z protocol for flash read ops
> + * @write_proto:	SPI x-y-z protocol for flash write ops
>    * @memory_map:		Address of read-only SPI flash access
>    * @flash_lock:		lock a region of the SPI Flash
>    * @flash_unlock:	unlock a region of the SPI Flash
> @@ -83,6 +85,8 @@ struct spi_flash {
>   	u8 read_cmd;
>   	u8 write_cmd;
>   	u8 dummy_byte;
> +	enum spi_flash_protocol read_proto;
> +	enum spi_flash_protocol write_proto;
>   
>   	void *memory_map;
>   

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 4/8] sf: differentiate Page Program 1-1-4 and 1-4-4
  2017-05-19 14:59 ` [U-Boot] [PATCH 4/8] sf: differentiate Page Program 1-1-4 and 1-4-4 Cyrille Pitchen
@ 2017-05-22  1:19   ` Yang, Wenyou
  0 siblings, 0 replies; 17+ messages in thread
From: Yang, Wenyou @ 2017-05-22  1:19 UTC (permalink / raw)
  To: u-boot



On 2017/5/19 22:59, Cyrille Pitchen wrote:
> This patch simply renames the ambiguous CMD_QUAD_PAGE_PROGRAM macro
> into the more explicit CMD_PAGE_PROGRAM_1_1_4.
> Also it defines the CMD_PAGE_PROGRAM_1_4_4 macro to the standard 38h op
> code.
>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

Acked-by Wenyou Yang <wenyou.yang@atmel.com>


Best Regards,
Wenyou Yang
> ---
>   drivers/mtd/spi/sf_internal.h | 3 ++-
>   drivers/mtd/spi/spi_flash.c   | 2 +-
>   2 files changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
> index 5c551089d673..8b8c951bcc55 100644
> --- a/drivers/mtd/spi/sf_internal.h
> +++ b/drivers/mtd/spi/sf_internal.h
> @@ -48,7 +48,8 @@ enum spi_nor_option_flags {
>   #define CMD_PAGE_PROGRAM		0x02
>   #define CMD_WRITE_DISABLE		0x04
>   #define CMD_WRITE_ENABLE		0x06
> -#define CMD_QUAD_PAGE_PROGRAM		0x32
> +#define CMD_PAGE_PROGRAM_1_1_4		0x32
> +#define CMD_PAGE_PROGRAM_1_4_4		0x38
>   
>   /* Read commands */
>   #define CMD_READ_ARRAY_SLOW		0x03
> diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
> index 7e35fb9f4802..ec998166017d 100644
> --- a/drivers/mtd/spi/spi_flash.c
> +++ b/drivers/mtd/spi/spi_flash.c
> @@ -1060,7 +1060,7 @@ int spi_flash_scan(struct spi_flash *flash)
>   
>   	/* Look for write commands */
>   	if (info->flags & WR_QPP && spi->mode & SPI_TX_QUAD) {
> -		flash->write_cmd = CMD_QUAD_PAGE_PROGRAM;
> +		flash->write_cmd = CMD_PAGE_PROGRAM_1_1_4;
>   		flash->write_proto = SPI_FPROTO_1_1_4;
>   	} else {
>   		/* Go for default supported write cmd */

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 5/8] sf: add 'addr_len' member to 'struct spi_flash'
  2017-05-19 14:59 ` [U-Boot] [PATCH 5/8] sf: add 'addr_len' member to 'struct spi_flash' Cyrille Pitchen
@ 2017-05-22  1:19   ` Yang, Wenyou
  0 siblings, 0 replies; 17+ messages in thread
From: Yang, Wenyou @ 2017-05-22  1:19 UTC (permalink / raw)
  To: u-boot



On 2017/5/19 22:59, Cyrille Pitchen wrote:
> This is a transitional patch to prepare the SPI FLASH sub-system to
> support the 4-byte address instruction set later.
> For now, flash->addr_len is always set to SPI_FLASH_3B_ADDR_LEN.
>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

Acked-by Wenyou Yang <wenyou.yang@atmel.com>


Best Regards,
Wenyou Yang
> ---
>   drivers/mtd/spi/spi_flash.c | 13 ++++++++-----
>   include/spi_flash.h         |  2 ++
>   2 files changed, 10 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
> index ec998166017d..c5e00772f241 100644
> --- a/drivers/mtd/spi/spi_flash.c
> +++ b/drivers/mtd/spi/spi_flash.c
> @@ -322,7 +322,7 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
>   		}
>   	}
>   
> -	spi_flash_command_init(&cmd, flash->erase_cmd, SPI_FLASH_3B_ADDR_LEN,
> +	spi_flash_command_init(&cmd, flash->erase_cmd, flash->addr_len,
>   			       SPI_FCMD_ERASE);
>   	while (len) {
>   		erase_addr = offset;
> @@ -377,7 +377,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
>   		}
>   	}
>   
> -	spi_flash_command_init(&cmd, flash->write_cmd, SPI_FLASH_3B_ADDR_LEN,
> +	spi_flash_command_init(&cmd, flash->write_cmd, flash->addr_len,
>   			       SPI_FCMD_WRITE);
>   	cmd.proto = flash->write_proto;
>   	for (actual = 0; actual < len; actual += chunk_len) {
> @@ -481,7 +481,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
>   		return 0;
>   	}
>   
> -	spi_flash_command_init(&cmd, flash->read_cmd, SPI_FLASH_3B_ADDR_LEN,
> +	spi_flash_command_init(&cmd, flash->read_cmd, flash->addr_len,
>   			       SPI_FCMD_READ);
>   	cmd.proto = flash->read_proto;
>   	cmd.num_wait_states = flash->dummy_byte * 8;
> @@ -529,7 +529,7 @@ static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf)
>   	int ret;
>   	u8 sr = 0xFFu;
>   
> -	spi_flash_command_init(&cmd, CMD_SST_BP, SPI_FLASH_3B_ADDR_LEN,
> +	spi_flash_command_init(&cmd, CMD_SST_BP, flash->addr_len,
>   			       SPI_FCMD_WRITE);
>   	cmd.addr = offset;
>   	cmd.data_len = 1;
> @@ -580,7 +580,7 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
>   	if (ret)
>   		goto done;
>   
> -	spi_flash_command_init(&cmd, CMD_SST_AAI_WP, SPI_FLASH_3B_ADDR_LEN,
> +	spi_flash_command_init(&cmd, CMD_SST_AAI_WP, flash->addr_len,
>   			       SPI_FCMD_WRITE);
>   	cmd.addr = offset;
>   	cmd.data_len = 2;
> @@ -1103,6 +1103,9 @@ int spi_flash_scan(struct spi_flash *flash)
>   		flash->flags |= SNOR_F_USE_FSR;
>   #endif
>   
> +	/* Set the address length */
> +	flash->addr_len = SPI_FLASH_3B_ADDR_LEN;
> +
>   	/* Configure the BAR - discover bank cmds and read current bank */
>   #ifdef CONFIG_SPI_FLASH_BAR
>   	ret = read_bar(flash, info);
> diff --git a/include/spi_flash.h b/include/spi_flash.h
> index ac2b37f0202f..9168fca8f96d 100644
> --- a/include/spi_flash.h
> +++ b/include/spi_flash.h
> @@ -44,6 +44,7 @@ struct spi_slave;
>    * @bank_read_cmd:	Bank read cmd
>    * @bank_write_cmd:	Bank write cmd
>    * @bank_curr:		Current flash bank
> + * @addr_len:		Number of bytes for the address
>    * @erase_cmd:		Erase cmd 4K, 32K, 64K
>    * @read_cmd:		Read cmd - Array Fast, Extn read and quad read.
>    * @write_cmd:		Write cmd - page and quad program.
> @@ -81,6 +82,7 @@ struct spi_flash {
>   	u8 bank_write_cmd;
>   	u8 bank_curr;
>   #endif
> +	u8 addr_len;
>   	u8 erase_cmd;
>   	u8 read_cmd;
>   	u8 write_cmd;

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 6/8] sf: add new option to support SPI flash above 16MiB
  2017-05-19 14:59 ` [U-Boot] [PATCH 6/8] sf: add new option to support SPI flash above 16MiB Cyrille Pitchen
@ 2017-05-22  1:20   ` Yang, Wenyou
  0 siblings, 0 replies; 17+ messages in thread
From: Yang, Wenyou @ 2017-05-22  1:20 UTC (permalink / raw)
  To: u-boot



On 2017/5/19 22:59, Cyrille Pitchen wrote:
> The patch provides an alternative method to support SPI flash size greater
> than 16MiB (128Mib).
>
> Indeed using the Base Address Register (BAR) is stateful. Hence, once the
> BAR has been modified, if a spurious CPU reset occurs with no reset/power
> off at the SPI flash side, early boot loarders may try to read from offset
> 0 but fails because of the new BAR value.
>
> On the other hand, using the 4-byte address instruction set is stateless.
> When supported by the SPI flash memory, it allows us to access memory data
> area above 16MiB without changing the internal state of this SPI flash
> memory. Then if a spirious reboot occurs, early boot loaders can still
> access data from offset 0.
>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

Acked-by Wenyou Yang <wenyou.yang@atmel.com>


Best Regards,
Wenyou Yang
> ---
>   drivers/mtd/spi/Kconfig       | 15 ++++++-
>   drivers/mtd/spi/sf_internal.h | 18 +++++++++
>   drivers/mtd/spi/spi_flash.c   | 92 ++++++++++++++++++++++++++++++++++++++++---
>   3 files changed, 118 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig
> index 5ca0a712d84a..c11e83263b16 100644
> --- a/drivers/mtd/spi/Kconfig
> +++ b/drivers/mtd/spi/Kconfig
> @@ -34,14 +34,27 @@ config SPI_FLASH
>   
>   	  If unsure, say N
>   
> +choice
> +	prompt "Support SPI flash above 16MiB"
> +	depends on SPI_FLASH
> +	optional
> +
>   config SPI_FLASH_BAR
>   	bool "SPI flash Bank/Extended address register support"
> -	depends on SPI_FLASH
>   	help
>   	  Enable the SPI flash Bank/Extended address register support.
>   	  Bank/Extended address registers are used to access the flash
>   	  which has size > 16MiB in 3-byte addressing.
>   
> +config SPI_FLASH_4BAIS
> +	bool "SPI flash 4-byte address instruction set support"
> +	help
> +	  Convert the selected 3-byte address op codes into their associated
> +	  4-byte address op codes. Using this instruction set does not change
> +	  the internal state of the SPI flash device.
> +
> +endchoice
> +
>   if SPI_FLASH
>   
>   config SPI_FLASH_ATMEL
> diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
> index 8b8c951bcc55..30994f9f460c 100644
> --- a/drivers/mtd/spi/sf_internal.h
> +++ b/drivers/mtd/spi/sf_internal.h
> @@ -27,6 +27,7 @@ enum spi_nor_option_flags {
>   };
>   
>   #define SPI_FLASH_3B_ADDR_LEN		3
> +#define SPI_FLASH_4B_ADDR_LEN		4
>   #define SPI_FLASH_CMD_LEN		(1 + SPI_FLASH_3B_ADDR_LEN + 16)
>   #define SPI_FLASH_16MB_BOUN		0x1000000
>   
> @@ -64,6 +65,19 @@ enum spi_nor_option_flags {
>   #define CMD_READ_CONFIG			0x35
>   #define CMD_FLAG_STATUS			0x70
>   
> +/* 4-byte address instruction set */
> +#define CMD_READ_ARRAY_SLOW_4B		0x13
> +#define CMD_READ_ARRAY_FAST_4B		0x0c
> +#define CMD_READ_DUAL_OUTPUT_FAST_4B	0x3c
> +#define CMD_READ_DUAL_IO_FAST_4B	0xbc
> +#define CMD_READ_QUAD_OUTPUT_FAST_4B	0x6c
> +#define CMD_READ_QUAD_IO_FAST_4B	0xec
> +#define CMD_PAGE_PROGRAM_4B		0x12
> +#define CMD_PAGE_PROGRAM_1_1_4_4B	0x34
> +#define CMD_PAGE_PROGRAM_1_4_4_4B	0x3e
> +#define CMD_ERASE_4K_4B			0x21
> +#define CMD_ERASE_64K_4B		0xdc
> +
>   /* Bank addr access commands */
>   #ifdef CONFIG_SPI_FLASH_BAR
>   # define CMD_BANKADDR_BRWR		0x17
> @@ -133,6 +147,10 @@ struct spi_flash_info {
>   #define RD_QUADIO		BIT(6)	/* use Quad IO Read */
>   #define RD_DUALIO		BIT(7)	/* use Dual IO Read */
>   #define RD_FULL			(RD_QUAD | RD_DUAL | RD_QUADIO | RD_DUALIO)
> +#define NO_4BAIS		BIT(8)	/*
> +					 * 4-byte address instruction set
> +					 * NOT supported
> +					 */
>   };
>   
>   extern const struct spi_flash_info spi_flash_ids[];
> diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
> index c5e00772f241..695c8555db3f 100644
> --- a/drivers/mtd/spi/spi_flash.c
> +++ b/drivers/mtd/spi/spi_flash.c
> @@ -176,6 +176,67 @@ bar_end:
>   }
>   #endif
>   
> +#ifdef CONFIG_SPI_FLASH_4BAIS
> +static u8 spi_flash_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
> +{
> +	size_t i;
> +
> +	for (i = 0; i < size; i++)
> +		if (table[i][0] == opcode)
> +			return table[i][1];
> +
> +	/* No conversion found, keep input op code. */
> +	return opcode;
> +}
> +
> +static u8 spi_flash_convert_3to4_read(u8 opcode)
> +{
> +	static const u8 spi_flash_3to4_read[][2] = {
> +		{CMD_READ_ARRAY_SLOW,		CMD_READ_ARRAY_SLOW_4B},
> +		{CMD_READ_ARRAY_FAST,		CMD_READ_ARRAY_FAST_4B},
> +		{CMD_READ_DUAL_OUTPUT_FAST,	CMD_READ_DUAL_OUTPUT_FAST_4B},
> +		{CMD_READ_DUAL_IO_FAST,		CMD_READ_DUAL_IO_FAST_4B},
> +		{CMD_READ_QUAD_OUTPUT_FAST,	CMD_READ_QUAD_OUTPUT_FAST_4B},
> +		{CMD_READ_QUAD_IO_FAST,		CMD_READ_QUAD_IO_FAST_4B},
> +	};
> +
> +	return spi_flash_convert_opcode(opcode, spi_flash_3to4_read,
> +					ARRAY_SIZE(spi_flash_3to4_read));
> +}
> +
> +static u8 spi_flash_convert_3to4_write(u8 opcode)
> +{
> +	static const u8 spi_flash_3to4_write[][2] = {
> +		{CMD_PAGE_PROGRAM,		CMD_PAGE_PROGRAM_4B},
> +		{CMD_PAGE_PROGRAM_1_1_4,	CMD_PAGE_PROGRAM_1_1_4_4B},
> +		{CMD_PAGE_PROGRAM_1_4_4,	CMD_PAGE_PROGRAM_1_4_4_4B},
> +	};
> +
> +	return spi_flash_convert_opcode(opcode, spi_flash_3to4_write,
> +					ARRAY_SIZE(spi_flash_3to4_write));
> +}
> +
> +static u8 spi_flash_convert_3to4_erase(u8 opcode)
> +{
> +	static const u8 spi_flash_3to4_erase[][2] = {
> +		{CMD_ERASE_4K,	CMD_ERASE_4K_4B},
> +		{CMD_ERASE_64K,	CMD_ERASE_64K_4B},
> +	};
> +
> +	return spi_flash_convert_opcode(opcode, spi_flash_3to4_erase,
> +					ARRAY_SIZE(spi_flash_3to4_erase));
> +}
> +
> +static void spi_flash_set_4byte_addr_opcodes(struct spi_flash *flash,
> +					     const struct spi_flash_info *info)
> +{
> +	flash->read_cmd = spi_flash_convert_3to4_read(flash->read_cmd);
> +	flash->write_cmd = spi_flash_convert_3to4_write(flash->write_cmd);
> +	flash->erase_cmd = spi_flash_convert_3to4_erase(flash->erase_cmd);
> +	flash->addr_len = SPI_FLASH_4B_ADDR_LEN;
> +}
> +#endif
> +
>   #ifdef CONFIG_SF_DUAL_FLASH
>   static void spi_flash_dual(struct spi_flash *flash, u32 *addr)
>   {
> @@ -966,6 +1027,7 @@ int spi_flash_scan(struct spi_flash *flash)
>   {
>   	struct spi_slave *spi = flash->spi;
>   	const struct spi_flash_info *info = NULL;
> +	bool above_16MB;
>   	int ret;
>   
>   	info = spi_flash_read_id(flash);
> @@ -1106,6 +1168,26 @@ int spi_flash_scan(struct spi_flash *flash)
>   	/* Set the address length */
>   	flash->addr_len = SPI_FLASH_3B_ADDR_LEN;
>   
> +	above_16MB = ((flash->dual_flash == SF_SINGLE_FLASH) &&
> +		      (flash->size > SPI_FLASH_16MB_BOUN)) ||
> +		     ((flash->dual_flash > SF_SINGLE_FLASH) &&
> +		      (flash->size > SPI_FLASH_16MB_BOUN << 1));
> +
> +	/*
> +	 * replace the selected 3-byte address op codes with the associated
> +	 * 4-byte address op codes, if needed (flash->size > 16 MiB)
> +	 */
> +#ifdef CONFIG_SPI_FLASH_4BAIS
> +	if (above_16MB) {
> +		if (info->flags & NO_4BAIS) {
> +			puts("SF: Warning - Only lower 16MiB accessible,");
> +			puts(" 4-byte address instruction set not supported\n");
> +		} else {
> +			spi_flash_set_4byte_addr_opcodes(flash, info);
> +		}
> +	}
> +#endif
> +
>   	/* Configure the BAR - discover bank cmds and read current bank */
>   #ifdef CONFIG_SPI_FLASH_BAR
>   	ret = read_bar(flash, info);
> @@ -1131,13 +1213,11 @@ int spi_flash_scan(struct spi_flash *flash)
>   	puts("\n");
>   #endif
>   
> -#ifndef CONFIG_SPI_FLASH_BAR
> -	if (((flash->dual_flash == SF_SINGLE_FLASH) &&
> -	     (flash->size > SPI_FLASH_16MB_BOUN)) ||
> -	     ((flash->dual_flash > SF_SINGLE_FLASH) &&
> -	     (flash->size > SPI_FLASH_16MB_BOUN << 1))) {
> +#if !defined(CONFIG_SPI_FLASH_BAR) && !defined(CONFIG_SPI_FLASH_4BAIS)
> +	if (above_16MB) {
>   		puts("SF: Warning - Only lower 16MiB accessible,");
> -		puts(" Full access #define CONFIG_SPI_FLASH_BAR\n");
> +		puts(" Full access #define CONFIG_SPI_FLASH_BAR");
> +		puts(" or CONFIG_SPI_FLASH_4BAIS\n");
>   	}
>   #endif
>   

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 7/8] sf: add support to Microchip SST26 QSPI memories
  2017-05-19 14:59 ` [U-Boot] [PATCH 7/8] sf: add support to Microchip SST26 QSPI memories Cyrille Pitchen
@ 2017-05-22  1:20   ` Yang, Wenyou
  0 siblings, 0 replies; 17+ messages in thread
From: Yang, Wenyou @ 2017-05-22  1:20 UTC (permalink / raw)
  To: u-boot



On 2017/5/19 22:59, Cyrille Pitchen wrote:
> This patch adds support to Microchip SST26 QSPI memories.
>
> Erase blocks are protected at power up and must be unlocked first before
> being erased then programmed.
>
> Also, the erase block sizes are not uniform. The memory layout is uniform
> only for the 4K sector blocks. The 64K Block Erase (D8h) op code cannot be
> used as currently done by the SPI FLASH sub-system.
> The 4K Sector Erase (20h) op code should be chosen instead even if
> CONFIG_SPI_FLASH_USE_4K_SECTORS is not set.
>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

Acked-by Wenyou Yang <wenyou.yang@atmel.com>


Best Regards,
Wenyou Yang
> ---
>   drivers/mtd/spi/sf_internal.h   |  3 +++
>   drivers/mtd/spi/spi_flash.c     | 33 ++++++++++++++++++++++++++++++---
>   drivers/mtd/spi/spi_flash_ids.c |  5 +++++
>   3 files changed, 38 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
> index 30994f9f460c..4354a2aa532f 100644
> --- a/drivers/mtd/spi/sf_internal.h
> +++ b/drivers/mtd/spi/sf_internal.h
> @@ -104,6 +104,7 @@ enum spi_nor_option_flags {
>   #ifdef CONFIG_SPI_FLASH_SST
>   # define CMD_SST_BP		0x02    /* Byte Program */
>   # define CMD_SST_AAI_WP		0xAD	/* Auto Address Incr Word Program */
> +# define CMD_SST_ULBPR		0x98	/* Global Block Protection Unlock */
>   
>   int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
>   		const void *buf);
> @@ -151,6 +152,8 @@ struct spi_flash_info {
>   					 * 4-byte address instruction set
>   					 * NOT supported
>   					 */
> +#define SECT_4K_ONLY		BIT(9)  /* use only CMD_ERASE_4K */
> +#define SST_ULBPR		BIT(10)	/* use SST unlock block protection */
>   };
>   
>   extern const struct spi_flash_info spi_flash_ids[];
> diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
> index 695c8555db3f..307e4140826b 100644
> --- a/drivers/mtd/spi/spi_flash.c
> +++ b/drivers/mtd/spi/spi_flash.c
> @@ -891,6 +891,22 @@ int stm_unlock(struct spi_flash *flash, u32 ofs, size_t len)
>   }
>   #endif
>   
> +#ifdef CONFIG_SPI_FLASH_SST
> +static int sst26_unlock(struct spi_flash *flash)
> +{
> +	struct spi_flash_command cmd;
> +	int ret;
> +
> +	spi_flash_command_init(&cmd, CMD_SST_ULBPR, 0, SPI_FCMD_WRITE_REG);
> +	ret = spi_flash_write_common(flash, &cmd);
> +	if (ret) {
> +		debug("SF: SST26 is still locked (read-only)\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +#endif
>   
>   #ifdef CONFIG_SPI_FLASH_MACRONIX
>   static int macronix_quad_enable(struct spi_flash *flash)
> @@ -920,7 +936,8 @@ static int macronix_quad_enable(struct spi_flash *flash)
>   }
>   #endif
>   
> -#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
> +#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND) ||\
> +    defined(CONFIG_SPI_FLASH_SST)
>   static int spansion_quad_enable(struct spi_flash *flash)
>   {
>   	u8 qeb_status;
> @@ -981,9 +998,11 @@ static int set_quad_mode(struct spi_flash *flash,
>   	case SPI_FLASH_CFI_MFR_MACRONIX:
>   		return macronix_quad_enable(flash);
>   #endif
> -#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
> +#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND) ||\
> +    defined(CONFIG_SPI_FLASH_SST)
>   	case SPI_FLASH_CFI_MFR_SPANSION:
>   	case SPI_FLASH_CFI_MFR_WINBOND:
> +	case SPI_FLASH_CFI_MFR_SST:
>   		return spansion_quad_enable(flash);
>   #endif
>   #ifdef CONFIG_SPI_FLASH_STMICRO
> @@ -1040,6 +1059,11 @@ int spi_flash_scan(struct spi_flash *flash)
>   	    JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SST)
>   		write_sr(flash, 0);
>   
> +#ifdef CONFIG_SPI_FLASH_SST
> +	if (info->flags & SST_ULBPR)
> +		sst26_unlock(flash);
> +#endif
> +
>   	flash->name = info->name;
>   	flash->memory_map = spi->memory_map;
>   
> @@ -1099,7 +1123,10 @@ int spi_flash_scan(struct spi_flash *flash)
>   		flash->erase_size = 4096 << flash->shift;
>   	} else
>   #endif
> -	{
> +	if (info->flags & SECT_4K_ONLY) {
> +		flash->erase_cmd = CMD_ERASE_4K;
> +		flash->erase_size = 4096 << flash->shift;
> +	} else {
>   		flash->erase_cmd = CMD_ERASE_64K;
>   		flash->erase_size = flash->sector_size;
>   	}
> diff --git a/drivers/mtd/spi/spi_flash_ids.c b/drivers/mtd/spi/spi_flash_ids.c
> index edca94e30cf0..3d3132bc3b22 100644
> --- a/drivers/mtd/spi/spi_flash_ids.c
> +++ b/drivers/mtd/spi/spi_flash_ids.c
> @@ -146,6 +146,11 @@ const struct spi_flash_info spi_flash_ids[] = {
>   	{"sst25wf040",	   INFO(0xbf2504, 0x0,	64 * 1024,     8, SECT_4K | SST_WR) },
>   	{"sst25wf040b",	   INFO(0x621613, 0x0,	64 * 1024,     8, SECT_4K) },
>   	{"sst25wf080",	   INFO(0xbf2505, 0x0,	64 * 1024,    16, SECT_4K | SST_WR) },
> +	{"sst26vf016b",	   INFO(0xbf2641, 0x0,   4 * 1024,   512, SECT_4K_ONLY | SST_ULBPR | RD_FULL) },
> +	{"sst26vf032b",	   INFO(0xbf2642, 0x0,   4 * 1024,  1024, SECT_4K_ONLY | SST_ULBPR | RD_FULL) },
> +	{"sst26vf064b",	   INFO(0xbf2643, 0x0,   4 * 1024,  2048, SECT_4K_ONLY | SST_ULBPR | RD_FULL) },
> +	{"sst26wf040b",	   INFO(0xbf2654, 0x0,   4 * 1024,   128, SECT_4K_ONLY | SST_ULBPR | RD_FULL) },
> +	{"sst26wf080b",	   INFO(0xbf2658, 0x0,   4 * 1024,   256, SECT_4K_ONLY | SST_ULBPR | RD_FULL) },
>   #endif
>   #ifdef CONFIG_SPI_FLASH_WINBOND		/* WINBOND */
>   	{"w25p80",	   INFO(0xef2014, 0x0,	64 * 1024,    16, 0) },

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [U-Boot] [PATCH 8/8] sf: add driver for Atmel QSPI controller
  2017-05-19 14:59 ` [U-Boot] [PATCH 8/8] sf: add driver for Atmel QSPI controller Cyrille Pitchen
@ 2017-05-22  1:21   ` Yang, Wenyou
  0 siblings, 0 replies; 17+ messages in thread
From: Yang, Wenyou @ 2017-05-22  1:21 UTC (permalink / raw)
  To: u-boot



On 2017/5/19 22:59, Cyrille Pitchen wrote:
> This patch adds support to the Atmel Quad SPI controller.
>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

Acked-by Wenyou Yang <wenyou.yang@atmel.com>


Best Regards,
Wenyou Yang
> ---
>   drivers/spi/Kconfig      |   7 +
>   drivers/spi/Makefile     |   1 +
>   drivers/spi/atmel_qspi.c | 404 +++++++++++++++++++++++++++++++++++++++++++++++
>   drivers/spi/atmel_qspi.h | 169 ++++++++++++++++++++
>   4 files changed, 581 insertions(+)
>   create mode 100644 drivers/spi/atmel_qspi.c
>   create mode 100644 drivers/spi/atmel_qspi.h
>
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index f3f7dbe0897b..73f2e5c26bfb 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -32,6 +32,13 @@ config ATH79_SPI
>   	  uses driver model and requires a device tree binding to operate.
>   	  please refer to doc/device-tree-bindings/spi/spi-ath79.txt.
>   
> +config ATMEL_QSPI
> +	bool "Atmel QSPI driver"
> +	depends on ARCH_AT91
> +	help
> +	  Enable the Ateml Quad-SPI (QSPI) driver. This driver can only be
> +	  used to access SPI NOR flashes.
> +
>   config ATMEL_SPI
>   	bool "Atmel SPI driver"
>   	depends on ARCH_AT91
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index c090562c7732..af75fa41c82f 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -18,6 +18,7 @@ endif
>   obj-$(CONFIG_ALTERA_SPI) += altera_spi.o
>   obj-$(CONFIG_ATH79_SPI) += ath79_spi.o
>   obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o
> +obj-$(CONFIG_ATMEL_QSPI) += atmel_qspi.o
>   obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o
>   obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
>   obj-$(CONFIG_CF_SPI) += cf_spi.o
> diff --git a/drivers/spi/atmel_qspi.c b/drivers/spi/atmel_qspi.c
> new file mode 100644
> index 000000000000..6c265d0a4714
> --- /dev/null
> +++ b/drivers/spi/atmel_qspi.c
> @@ -0,0 +1,404 @@
> +/*
> + * Copyright (C) 2017 Atmel Corporation
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +#include <common.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <fdtdec.h>
> +#include <errno.h>
> +#include <spi.h>
> +#include <asm/io.h>
> +#include <mach/clk.h>
> +#include "atmel_qspi.h"
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +static void atmel_qspi_memcpy_fromio(void *dst, unsigned long src, size_t len)
> +{
> +	u8 *d = (u8 *)dst;
> +
> +	while (len--) {
> +		*d++ = readb(src);
> +		src++;
> +	}
> +}
> +
> +static void atmel_qspi_memcpy_toio(unsigned long dst, const void *src,
> +				   size_t len)
> +{
> +	const u8 *s = (const u8 *)src;
> +
> +	while (len--) {
> +		writeb(*s, dst);
> +		dst++;
> +		s++;
> +	}
> +}
> +
> +static int atmel_qspi_set_ifr_tfrtype(u8 flags, u32 *ifr)
> +{
> +	u32 ifr_tfrtype;
> +
> +	switch (flags & SPI_FCMD_TYPE) {
> +	case SPI_FCMD_READ:
> +		ifr_tfrtype = QSPI_IFR_TFRTYPE_READ_MEMORY;
> +		break;
> +
> +	case SPI_FCMD_WRITE:
> +		ifr_tfrtype = QSPI_IFR_TFRTYPE_WRITE_MEMORY;
> +		break;
> +
> +	case SPI_FCMD_ERASE:
> +	case SPI_FCMD_WRITE_REG:
> +		ifr_tfrtype = QSPI_IFR_TFRTYPE_WRITE;
> +		break;
> +
> +	case SPI_FCMD_READ_REG:
> +		ifr_tfrtype = QSPI_IFR_TFRTYPE_READ;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	*ifr = (*ifr & ~QSPI_IFR_TFRTYPE) | ifr_tfrtype;
> +	return 0;
> +}
> +
> +static int atmel_qpsi_set_ifr_width(enum spi_flash_protocol proto, u32 *ifr)
> +{
> +	u32 ifr_width;
> +
> +	switch (proto) {
> +	case SPI_FPROTO_1_1_1:
> +		ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
> +		break;
> +
> +	case SPI_FPROTO_1_1_2:
> +		ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
> +		break;
> +
> +	case SPI_FPROTO_1_2_2:
> +		ifr_width = QSPI_IFR_WIDTH_DUAL_IO;
> +		break;
> +
> +	case SPI_FPROTO_2_2_2:
> +		ifr_width = QSPI_IFR_WIDTH_DUAL_CMD;
> +		break;
> +
> +	case SPI_FPROTO_1_1_4:
> +		ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
> +		break;
> +
> +	case SPI_FPROTO_1_4_4:
> +		ifr_width = QSPI_IFR_WIDTH_QUAD_IO;
> +		break;
> +
> +	case SPI_FPROTO_4_4_4:
> +		ifr_width = QSPI_IFR_WIDTH_QUAD_CMD;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	*ifr = (*ifr & ~QSPI_IFR_WIDTH) | ifr_width;
> +	return 0;
> +}
> +
> +static int atmel_qspi_xfer(struct udevice *dev, unsigned int bitlen,
> +			   const void *dout, void *din, unsigned long flags)
> +{
> +	/* This controller can only be used with SPI NOR flashes. */
> +	return -EINVAL;
> +}
> +
> +static int atmel_qspi_set_speed(struct udevice *bus, uint hz)
> +{
> +	struct atmel_qspi_priv *aq = dev_get_priv(bus);
> +	u32 scr, scbr, mask, new_value;
> +
> +	/* Compute the QSPI baudrate */
> +	scbr = DIV_ROUND_UP(aq->bus_clk_rate, hz);
> +	if (scbr > 0)
> +		scbr--;
> +
> +	new_value = QSPI_SCR_SCBR_(scbr);
> +	mask = QSPI_SCR_SCBR;
> +
> +	scr = qspi_readl(aq, QSPI_SCR);
> +	if ((scr & mask) == new_value)
> +		return 0;
> +
> +	scr = (scr & ~mask) | new_value;
> +	qspi_writel(aq, QSPI_SCR, scr);
> +
> +	return 0;
> +}
> +
> +static int atmel_qspi_set_mode(struct udevice *bus, uint mode)
> +{
> +	struct atmel_qspi_priv *aq = dev_get_priv(bus);
> +	u32 scr, mask, new_value;
> +
> +	new_value = (QSPI_SCR_CPOL_((mode & SPI_CPOL) != 0) |
> +		     QSPI_SCR_CPHA_((mode & SPI_CPHA) != 0));
> +	mask = (QSPI_SCR_CPOL | QSPI_SCR_CPHA);
> +
> +	scr = qspi_readl(aq, QSPI_SCR);
> +	if ((scr & mask) == new_value)
> +		return 0;
> +
> +	scr = (scr & ~mask) | new_value;
> +	qspi_writel(aq, QSPI_SCR, scr);
> +
> +	return 0;
> +}
> +
> +static bool
> +atmel_qspi_is_flash_command_supported(struct udevice *dev,
> +				      const struct spi_flash_command *cmd)
> +{
> +	return true;
> +}
> +
> +static int atmel_qspi_exec_flash_command(struct udevice *dev,
> +					 const struct spi_flash_command *cmd)
> +{
> +	struct udevice *bus = dev_get_parent(dev);
> +	struct atmel_qspi_priv *aq = dev_get_priv(bus);
> +	unsigned int iar, icr, ifr;
> +	unsigned int offset;
> +	unsigned int imr, sr;
> +	unsigned long memaddr;
> +	int err;
> +
> +	iar = 0;
> +	icr = 0;
> +	ifr = 0;
> +
> +	err = atmel_qspi_set_ifr_tfrtype(cmd->flags, &ifr);
> +	if (err)
> +		return err;
> +
> +	err = atmel_qpsi_set_ifr_width(cmd->proto, &ifr);
> +	if (err)
> +		return err;
> +
> +	/* Compute instruction parameters */
> +	icr |= QSPI_ICR_INST_(cmd->inst);
> +	ifr |= QSPI_IFR_INSTEN;
> +
> +	/* Compute address parameters. */
> +	switch (cmd->addr_len) {
> +	case 4:
> +		ifr |= QSPI_IFR_ADDRL_32_BIT;
> +		/*break;*/ /* fall through the 24bit (3 byte) address case */
> +	case 3:
> +		iar = cmd->data_len ? 0 : cmd->addr;
> +		ifr |= QSPI_IFR_ADDREN;
> +		offset = cmd->addr;
> +		break;
> +	case 0:
> +		offset = 0;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/* Compute option parameters. */
> +	if (cmd->num_mode_cycles) {
> +		unsigned int mode_cycle_bits, mode_bits;
> +
> +		icr |= QSPI_ICR_OPT_(cmd->mode);
> +		ifr |= QSPI_IFR_OPTEN;
> +
> +		switch (ifr & QSPI_IFR_WIDTH) {
> +		case QSPI_IFR_WIDTH_SINGLE_BIT_SPI:
> +		case QSPI_IFR_WIDTH_DUAL_OUTPUT:
> +		case QSPI_IFR_WIDTH_QUAD_OUTPUT:
> +			mode_cycle_bits = 1;
> +			break;
> +		case QSPI_IFR_WIDTH_DUAL_IO:
> +		case QSPI_IFR_WIDTH_DUAL_CMD:
> +			mode_cycle_bits = 2;
> +			break;
> +		case QSPI_IFR_WIDTH_QUAD_IO:
> +		case QSPI_IFR_WIDTH_QUAD_CMD:
> +			mode_cycle_bits = 4;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +
> +		mode_bits = cmd->num_mode_cycles * mode_cycle_bits;
> +		switch (mode_bits) {
> +		case 1:
> +			ifr |= QSPI_IFR_OPTL_1BIT;
> +			break;
> +
> +		case 2:
> +			ifr |= QSPI_IFR_OPTL_2BIT;
> +			break;
> +
> +		case 4:
> +			ifr |= QSPI_IFR_OPTL_4BIT;
> +			break;
> +
> +		case 8:
> +			ifr |= QSPI_IFR_OPTL_8BIT;
> +			break;
> +
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* Set the number of dummy cycles. */
> +	if (cmd->num_wait_states)
> +		ifr |= QSPI_IFR_NBDUM_(cmd->num_wait_states);
> +
> +	/* Set data enable. */
> +	if (cmd->data_len)
> +		ifr |= QSPI_IFR_DATAEN;
> +
> +	/* Clear pending interrupts. */
> +	(void)qspi_readl(aq, QSPI_SR);
> +
> +	/* Set QSPI Instruction Frame registers. */
> +	qspi_writel(aq, QSPI_IAR, iar);
> +	qspi_writel(aq, QSPI_ICR, icr);
> +	qspi_writel(aq, QSPI_IFR, ifr);
> +
> +	/* Skip to the final steps if there is no data. */
> +	if (!cmd->data_len)
> +		goto no_data;
> +
> +	/* Dummy read of QSPI_IFR to synchronize APB and AHB accesses. */
> +	(void)qspi_readl(aq, QSPI_IFR);
> +
> +	/* Stop here for Continuous Read. */
> +	memaddr = (unsigned long)(aq->membase + offset);
> +	if (cmd->tx_data)
> +		/* Write data. */
> +		atmel_qspi_memcpy_toio(memaddr, cmd->tx_data, cmd->data_len);
> +	else if (cmd->rx_data)
> +		/* Read data. */
> +		atmel_qspi_memcpy_fromio(cmd->rx_data, memaddr, cmd->data_len);
> +
> +	/* Release the chip-select. */
> +	qspi_writel(aq, QSPI_CR, QSPI_CR_LASTXFER);
> +
> +no_data:
> +	/* Poll INSTruction End and Chip Select Rise flags. */
> +	imr = QSPI_SR_INSTRE | QSPI_SR_CSR;
> +	sr = 0;
> +	while (sr != (QSPI_SR_INSTRE | QSPI_SR_CSR))
> +		sr |= qspi_readl(aq, QSPI_SR) & imr;
> +
> +	return 0;
> +}
> +
> +
> +static const struct dm_spi_ops atmel_qspi_ops = {
> +	.xfer				= atmel_qspi_xfer,
> +	.set_speed			= atmel_qspi_set_speed,
> +	.set_mode			= atmel_qspi_set_mode,
> +	.is_flash_command_supported	= atmel_qspi_is_flash_command_supported,
> +	.exec_flash_command		= atmel_qspi_exec_flash_command,
> +};
> +
> +static int atmel_qspi_enable_clk(struct udevice *bus)
> +{
> +	struct atmel_qspi_priv *aq = dev_get_priv(bus);
> +	struct clk clk;
> +	ulong clk_rate;
> +	int ret;
> +
> +	ret = clk_get_by_index(bus, 0, &clk);
> +	if (ret)
> +		return -EINVAL;
> +
> +	ret = clk_enable(&clk);
> +	if (ret)
> +		goto free_clock;
> +
> +	clk_rate = clk_get_rate(&clk);
> +	if (!clk_rate) {
> +		ret = -EINVAL;
> +		goto free_clock;
> +	}
> +
> +	aq->bus_clk_rate = clk_rate;
> +
> +free_clock:
> +	clk_free(&clk);
> +
> +	return ret;
> +}
> +
> +static int atmel_qspi_probe(struct udevice *bus)
> +{
> +	const struct atmel_qspi_platdata *plat = dev_get_platdata(bus);
> +	struct atmel_qspi_priv *aq = dev_get_priv(bus);
> +	u32 mr;
> +	int ret;
> +
> +	ret = atmel_qspi_enable_clk(bus);
> +	if (ret)
> +		return ret;
> +
> +	aq->regbase = plat->regbase;
> +	aq->membase = plat->membase;
> +
> +	/* Reset the QSPI controler */
> +	qspi_writel(aq, QSPI_CR, QSPI_CR_SWRST);
> +
> +	/* Set the QSPI controller in Serial Memory Mode */
> +	mr = (QSPI_MR_NBBITS_8_BIT |
> +	      QSPI_MR_SMM_MEMORY |
> +	      QSPI_MR_CSMODE_LASTXFER);
> +	qspi_writel(aq, QSPI_MR, mr);
> +
> +	/* Enable the QSPI controller */
> +	qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIEN);
> +
> +	return 0;
> +}
> +
> +static int atmel_qspi_ofdata_to_platdata(struct udevice *bus)
> +{
> +	struct atmel_qspi_platdata *plat = dev_get_platdata(bus);
> +	const void *blob = gd->fdt_blob;
> +	int node = bus->of_offset;
> +	u32 data[4];
> +	int ret;
> +
> +	ret = fdtdec_get_int_array(blob, node, "reg", data, ARRAY_SIZE(data));
> +	if (ret) {
> +		printf("Error: Can't get base addresses (ret=%d)!\n", ret);
> +		return -ENODEV;
> +	}
> +	plat->regbase = (void *)data[0];
> +	plat->membase = (void *)data[2];
> +
> +	return 0;
> +}
> +
> +static const struct udevice_id atmel_qspi_ids[] = {
> +	{ .compatible = "atmel,sama5d2-qspi" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(atmel_qspi) = {
> +	.name		= "atmel_qspi",
> +	.id		= UCLASS_SPI,
> +	.of_match	= atmel_qspi_ids,
> +	.ops		= &atmel_qspi_ops,
> +	.ofdata_to_platdata = atmel_qspi_ofdata_to_platdata,
> +	.platdata_auto_alloc_size = sizeof(struct atmel_qspi_platdata),
> +	.priv_auto_alloc_size = sizeof(struct atmel_qspi_priv),
> +	.probe		= atmel_qspi_probe,
> +};
> diff --git a/drivers/spi/atmel_qspi.h b/drivers/spi/atmel_qspi.h
> new file mode 100644
> index 000000000000..ee1a14bd726d
> --- /dev/null
> +++ b/drivers/spi/atmel_qspi.h
> @@ -0,0 +1,169 @@
> +/*
> + * Copyright (C) 2016
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#ifndef __ATMEL_QSPI_H__
> +#define __ATMEL_QSPI_H__
> +
> +/*
> + * Register Definitions
> + */
> +#define	QSPI_CR		0x00	/* Control Register */
> +#define	QSPI_MR		0x04	/* Mode Register */
> +#define	QSPI_RDR	0x08	/* Receive Data Register */
> +#define	QSPI_TDR	0x0c	/* Transmit Data Register */
> +#define	QSPI_SR		0x10	/* Status Register */
> +#define	QSPI_IER	0x14	/* Interrupt Enable Register */
> +#define	QSPI_IDR	0x18	/* Interrupt Disable Register */
> +#define	QSPI_IMR	0x1c	/* Interrupt Mask Register */
> +#define	QSPI_SCR	0x20	/* Serial Clock Register */
> +#define	QSPI_IAR	0x30	/* Instruction Address Register */
> +#define	QSPI_ICR	0x34	/* Instruction Code Register */
> +#define	QSPI_IFR	0x38	/* Instruction Frame Register */
> +/* 0x3c Reserved */
> +#define	QSPI_SMR	0x40	/* Scrambling Mode Register */
> +#define	QSPI_SKR	0x44	/* Scrambling Key Register */
> +/* 0x48 ~ 0xe0 */
> +#define	QSPI_WPMR	0xe4	/* Write Protection Mode Register */
> +#define	QSPI_WPSR	0xe8	/* Write Protection Status Register */
> +/* 0xec ~ 0xf8 Reserved */
> +/* 0xfc Reserved */
> +
> +/*
> + * Register Field Definitions
> + */
> +/* QSPI_CR */
> +#define	QSPI_CR_QSPIEN		BIT(0)	/* QSPI Enable */
> +#define	QSPI_CR_QSPIDIS		BIT(1)	/* QSPI Disable */
> +#define	QSPI_CR_SWRST		BIT(7)	/* QSPI Software Reset */
> +#define	QSPI_CR_LASTXFER	BIT(24)	/* Last Transfer */
> +
> +/* QSPI_MR */
> +#define	QSPI_MR_SMM		BIT(0)	/* Serial Memort Mode */
> +#define		QSPI_MR_SMM_SPI		0
> +#define		QSPI_MR_SMM_MEMORY	QSPI_MR_SMM
> +#define	QSPI_MR_LLB		BIT(1)	/* Local Localback Enable */
> +#define		QSPI_MR_LLB_DISABLED	0
> +#define		QSPI_MR_LLB_ENABLED	QSPI_MR_LLB
> +#define	QSPI_MR_WDRBT		BIT(2)	/* Wait Data Read Before Transfer */
> +#define		QSPI_MR_WDRBT_DISABLED	0
> +#define		QSPI_MR_WDRBT_ENABLED	QSPI_MR_WDRBT
> +#define	QSPI_MR_SMRM		BIT(3)	/* Serial Memory Register Mode */
> +#define		QSPI_MR_SMRM_AHB	0
> +#define		QSPI_MR_SMRM_APB	QSPI_MR_SMRM
> +#define	QSPI_MR_CSMODE		GENMASK(5, 4)	/* Chip Select Mode */
> +#define		QSPI_MR_CSMODE_NOT_RELOADED	(0x0u << 4)
> +#define		QSPI_MR_CSMODE_LASTXFER		(0x1u << 4)
> +#define		QSPI_MR_CSMODE_SYSTEMATICALLY	(0x2u << 4)
> +#define	QSPI_MR_NBBITS		GENMASK(11, 8)	/*
> +						 * Number of Bits Per
> +						 * Transfer
> +						 */
> +#define		QSPI_MR_NBBITS_8_BIT		(0x0u << 8)
> +#define		QSPI_MR_NBBITS_16_BIT		(0x8u << 8)
> +#define	QSPI_MR_DLYBCT		GENMASK(23, 16)	/*
> +						 * Delay Between Consecutive
> +						 * Transfers
> +						 */
> +#define	QSPI_MR_DLYCS		GENMASK(31, 24)	/* Minimum Inactive QCS Delay */
> +
> +/* QSPI_SR */
> +#define	QSPI_SR_RDRF		BIT(0)	/* Receive Data Register Full */
> +#define	QSPI_SR_TDRE		BIT(1)	/* Transmit Data Register Empty */
> +#define	QSPI_SR_TXEMPTY		BIT(2)	/* Transmission Registers Empty */
> +#define	QSPI_SR_OVRES		BIT(3)	/* Overrun Error Status */
> +#define	QSPI_SR_CSR		BIT(8)	/* Chip Select Rise */
> +#define	QSPI_SR_CSS		BIT(9)	/* Chip Select Status */
> +#define	QSPI_SR_INSTRE		BIT(10)	/* Instruction End Status */
> +#define	QSPI_SR_QSPIENS		BIT(24)	/* QSPI Enable Status */
> +
> +/* QSPI_SCR */
> +#define	QSPI_SCR_CPOL		BIT(0)	/* Clock Polarity */
> +#define	QSPI_SCR_CPOL_(x)	((x) << 0)
> +#define	QSPI_SCR_CPHA		BIT(1)	/* Clock Phase */
> +#define	QSPI_SCR_CPHA_(x)	((x) << 1)
> +#define	QSPI_SCR_SCBR		GENMASK(15, 8)	/* Serial Clock Baud Rate */
> +#define	QSPI_SCR_SCBR_(x)	(((x) << 8) & QSPI_SCR_SCBR)
> +#define QSPI_SCR_DLYBS		GENMASK(23, 16)
> +#define	QSPI_SCR_DLYBS_(x)	(((x) << 16) & QSPI_SCR_DLYBS)	/*
> +								 * Delay Before
> +								 * QSCK
> +								 */
> +
> +/* QSPI_ICR */
> +#define QSPI_ICR_INST		GENMASK(7, 0)
> +#define	QSPI_ICR_INST_(x)	(((x) << 0) & QSPI_ICR_INST)	/*
> +								 * Instruction
> +								 * Code
> +								 */
> +#define QSPI_ICR_OPT		GENMASK(23, 16)
> +#define	QSPI_ICR_OPT_(x)	(((x) << 16) & QSPI_ICR_OPT)	/*
> +								 * Option
> +								 * Code
> +								 */
> +
> +/* QSPI_IFR */
> +#define	QSPI_IFR_WIDTH		GENMASK(2, 0)	/*
> +						 * Width of Instruction Code,
> +						 * Address, Option Code and Data
> +						 */
> +#define		QSPI_IFR_WIDTH_SINGLE_BIT_SPI	(0x0u << 0)
> +#define		QSPI_IFR_WIDTH_DUAL_OUTPUT	(0x1u << 0)
> +#define		QSPI_IFR_WIDTH_QUAD_OUTPUT	(0x2u << 0)
> +#define		QSPI_IFR_WIDTH_DUAL_IO		(0x3u << 0)
> +#define		QSPI_IFR_WIDTH_QUAD_IO		(0x4u << 0)
> +#define		QSPI_IFR_WIDTH_DUAL_CMD		(0x5u << 0)
> +#define		QSPI_IFR_WIDTH_QUAD_CMD		(0x6u << 0)
> +#define QSPI_IFR_WIDTH_(x)	(((x) << 0) & QSPI_IFR_WIDTH)
> +#define	QSPI_IFR_INSTEN		BIT(4)	/* Instruction Enable*/
> +#define	QSPI_IFR_ADDREN		BIT(5)	/* Address Enable*/
> +#define	QSPI_IFR_OPTEN		BIT(6)	/* Option Enable*/
> +#define	QSPI_IFR_DATAEN		BIT(7)	/* Data Enable*/
> +#define	QSPI_IFR_OPTL		GENMASK(9, 8)	/* Option Code Length */
> +#define		QSPI_IFR_OPTL_1BIT		(0x0u << 8)
> +#define		QSPI_IFR_OPTL_2BIT		(0x1u << 8)
> +#define		QSPI_IFR_OPTL_4BIT		(0x2u << 8)
> +#define		QSPI_IFR_OPTL_8BIT		(0x3u << 8)
> +#define	QSPI_IFR_ADDRL		BIT(10)	/* Address Length */
> +#define		QSPI_IFR_ADDRL_24_BIT		0
> +#define		QSPI_IFR_ADDRL_32_BIT		QSPI_IFR_ADDRL
> +#define	QSPI_IFR_TFRTYPE	GENMASK(13, 12)	/* Data Transfer Type */
> +#define		QSPI_IFR_TFRTYPE_READ		(0x0u << 12)
> +#define		QSPI_IFR_TFRTYPE_READ_MEMORY	(0x1u << 12)
> +#define		QSPI_IFR_TFRTYPE_WRITE		(0x2u << 12)
> +#define		QSPI_IFR_TFRTYPE_WRITE_MEMORY	(0x3u << 12)
> +#define QSPI_IFR_TFRTYPE_(x)	(((x) << 12) & QSPI_IFR_TFRTYPE)
> +#define	QSPI_IFR_CRM		BIT(14)	/* Continuous Read Mode */
> +#define QSPI_IFR_NBDUM		GENMASK(20, 16)
> +#define	QSPI_IFR_NBDUM_(x)	(((x) << 16) & QSPI_IFR_NBDUM)	/*
> +								 * Number Of
> +								 * Dummy Cycles
> +								 */
> +
> +
> +struct atmel_qspi_platdata {
> +	void		*regbase;
> +	void		*membase;
> +};
> +
> +struct atmel_qspi_priv {
> +	ulong		bus_clk_rate;
> +	void		*regbase;
> +	void		*membase;
> +};
> +
> +#include <asm/io.h>
> +
> +static inline u32 qspi_readl(struct atmel_qspi_priv *aq, u32 reg)
> +{
> +	return readl(aq->regbase + reg);
> +}
> +
> +static inline void qspi_writel(struct atmel_qspi_priv *aq, u32 reg, u32 value)
> +{
> +	writel(value, aq->regbase + reg);
> +}
> +
> +#endif /* __ATMEL_QSPI_H__ */

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2017-05-22  1:21 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-19 14:59 [U-Boot] [PATCH 0/8] sf: improve support of (Q)SPI flash memories Cyrille Pitchen
2017-05-19 14:59 ` [U-Boot] [PATCH 1/8] spi: add support of SPI flash commands Cyrille Pitchen
2017-05-22  1:17   ` Yang, Wenyou
2017-05-19 14:59 ` [U-Boot] [PATCH 2/8] sf: describe all SPI flash commands with 'struct spi_flash_command' Cyrille Pitchen
2017-05-22  1:18   ` Yang, Wenyou
2017-05-19 14:59 ` [U-Boot] [PATCH 3/8] sf: select the relevant SPI flash protocol for read and write commands Cyrille Pitchen
2017-05-22  1:18   ` Yang, Wenyou
2017-05-19 14:59 ` [U-Boot] [PATCH 4/8] sf: differentiate Page Program 1-1-4 and 1-4-4 Cyrille Pitchen
2017-05-22  1:19   ` Yang, Wenyou
2017-05-19 14:59 ` [U-Boot] [PATCH 5/8] sf: add 'addr_len' member to 'struct spi_flash' Cyrille Pitchen
2017-05-22  1:19   ` Yang, Wenyou
2017-05-19 14:59 ` [U-Boot] [PATCH 6/8] sf: add new option to support SPI flash above 16MiB Cyrille Pitchen
2017-05-22  1:20   ` Yang, Wenyou
2017-05-19 14:59 ` [U-Boot] [PATCH 7/8] sf: add support to Microchip SST26 QSPI memories Cyrille Pitchen
2017-05-22  1:20   ` Yang, Wenyou
2017-05-19 14:59 ` [U-Boot] [PATCH 8/8] sf: add driver for Atmel QSPI controller Cyrille Pitchen
2017-05-22  1:21   ` Yang, Wenyou

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.