[U-Boot] [PATCH 1/8] spi: add support of SPI flash commands

Cyrille Pitchen cyrille.pitchen at atmel.com
Fri May 19 14:59:39 UTC 2017


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 at 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



More information about the U-Boot mailing list