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

Yang, Wenyou Wenyou.Yang at Microchip.com
Mon May 22 01:17:39 UTC 2017



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 at atmel.com>

Acked-by Wenyou Yang <wenyou.yang at 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)



More information about the U-Boot mailing list