[PATCH v4 01/24] spi-mem: sync a bit with linux-6.16

Frieder Schrempf frieder.schrempf at kontron.de
Tue Aug 12 11:40:14 CEST 2025


Am 09.08.25 um 03:04 schrieb Mikhail Kshevetskiy:
> This also fix 8-wire support a bit.
> 
> Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>

Please change the subject prefix to "spi: spi-mem:". And I suggest the
following more meaningful commit message:

###
spi: spi-mem: Extend SPI MEM ops to match Linux 6.16

This pulls in multiple changes from the Linux kernel in order to keep
the code in sync. This also fixes octal mode support.
###

Otherwise this looks good to me. Feel free to add:

Reviewed-by: Frieder Schrempf <frieder.schrempf at kontron.de>

> ---
>  drivers/spi/spi-mem.c | 13 +++---
>  include/spi-mem.h     | 92 ++++++++++++++++++++++++++++++++++++++-----
>  2 files changed, 90 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
> index 3579b7d7db5..6f06650384c 100644
> --- a/drivers/spi/spi-mem.c
> +++ b/drivers/spi/spi-mem.c
> @@ -120,18 +120,21 @@ static int spi_check_buswidth_req(struct spi_slave *slave, u8 buswidth, bool tx)
>  		return 0;
>  
>  	case 2:
> -		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> -		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
> +		if ((tx &&
> +		     (mode & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL))) ||
> +		    (!tx &&
> +		     (mode & (SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL))))
>  			return 0;
>  
>  		break;
>  
>  	case 4:
> -		if ((tx && (mode & SPI_TX_QUAD)) ||
> -		    (!tx && (mode & SPI_RX_QUAD)))
> +		if ((tx && (mode & (SPI_TX_QUAD | SPI_TX_OCTAL))) ||
> +		    (!tx && (mode & (SPI_RX_QUAD | SPI_RX_OCTAL))))
>  			return 0;
>  
>  		break;
> +
>  	case 8:
>  		if ((tx && (mode & SPI_TX_OCTAL)) ||
>  		    (!tx && (mode & SPI_RX_OCTAL)))
> @@ -300,7 +303,7 @@ int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
>  		 * read path) and expect the core to use the regular SPI
>  		 * interface in other cases.
>  		 */
> -		if (!ret || ret != -ENOTSUPP) {
> +		if (!ret || (ret != -ENOTSUPP && ret != -EOPNOTSUPP)) {
>  			spi_release_bus(slave);
>  			return ret;
>  		}
> diff --git a/include/spi-mem.h b/include/spi-mem.h
> index 2eb05a2e5bc..e68e2b50e85 100644
> --- a/include/spi-mem.h
> +++ b/include/spi-mem.h
> @@ -17,16 +17,32 @@ struct udevice;
>  
>  #define SPI_MEM_OP_CMD(__opcode, __buswidth)			\
>  	{							\
> +		.nbytes = 1,					\
>  		.buswidth = __buswidth,				\
>  		.opcode = __opcode,				\
> +	}
> +
> +#define SPI_MEM_DTR_OP_CMD(__opcode, __buswidth)		\
> +	{							\
>  		.nbytes = 1,					\
> +		.opcode = __opcode,				\
> +		.buswidth = __buswidth,				\
> +		.dtr = true,					\
>  	}
>  
>  #define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth)		\
> +	{							\
> +		.nbytes = __nbytes,				\
> +		.buswidth = __buswidth,				\
> +		.val = __val,					\
> +	}
> +
> +#define SPI_MEM_DTR_OP_ADDR(__nbytes, __val, __buswidth)	\
>  	{							\
>  		.nbytes = __nbytes,				\
>  		.val = __val,					\
>  		.buswidth = __buswidth,				\
> +		.dtr = true,					\
>  	}
>  
>  #define SPI_MEM_OP_NO_ADDR	{ }
> @@ -37,22 +53,47 @@ struct udevice;
>  		.buswidth = __buswidth,				\
>  	}
>  
> +#define SPI_MEM_DTR_OP_DUMMY(__nbytes, __buswidth)		\
> +	{							\
> +		.nbytes = __nbytes,				\
> +		.buswidth = __buswidth,				\
> +		.dtr = true,					\
> +	}
> +
>  #define SPI_MEM_OP_NO_DUMMY	{ }
>  
>  #define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth)		\
> +	{							\
> +		.buswidth = __buswidth,				\
> +		.dir = SPI_MEM_DATA_IN,				\
> +		.nbytes = __nbytes,				\
> +		.buf.in = __buf,				\
> +	}
> +
> +#define SPI_MEM_DTR_OP_DATA_IN(__nbytes, __buf, __buswidth)	\
>  	{							\
>  		.dir = SPI_MEM_DATA_IN,				\
>  		.nbytes = __nbytes,				\
>  		.buf.in = __buf,				\
>  		.buswidth = __buswidth,				\
> +		.dtr = true,					\
>  	}
>  
>  #define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth)	\
> +	{							\
> +		.buswidth = __buswidth,				\
> +		.dir = SPI_MEM_DATA_OUT,			\
> +		.nbytes = __nbytes,				\
> +		.buf.out = __buf,				\
> +	}
> +
> +#define SPI_MEM_DTR_OP_DATA_OUT(__nbytes, __buf, __buswidth)	\
>  	{							\
>  		.dir = SPI_MEM_DATA_OUT,			\
>  		.nbytes = __nbytes,				\
>  		.buf.out = __buf,				\
>  		.buswidth = __buswidth,				\
> +		.dtr = true,					\
>  	}
>  
>  #define SPI_MEM_OP_NO_DATA	{ }
> @@ -62,7 +103,7 @@ struct udevice;
>   *			   transfer from the controller perspective
>   * @SPI_MEM_NO_DATA: no data transferred
>   * @SPI_MEM_DATA_IN: data coming from the SPI memory
> - * @SPI_MEM_DATA_OUT: data sent the SPI memory
> + * @SPI_MEM_DATA_OUT: data sent to the SPI memory
>   */
>  enum spi_mem_data_dir {
>  	SPI_MEM_NO_DATA,
> @@ -70,6 +111,9 @@ enum spi_mem_data_dir {
>  	SPI_MEM_DATA_OUT,
>  };
>  
> +#define SPI_MEM_OP_MAX_FREQ(__freq)				\
> +	.max_freq = __freq
> +
>  /**
>   * struct spi_mem_op - describes a SPI memory operation
>   * @cmd.nbytes: number of opcode bytes (only 1 or 2 are valid). The opcode is
> @@ -80,26 +124,35 @@ enum spi_mem_data_dir {
>   * @addr.nbytes: number of address bytes to send. Can be zero if the operation
>   *		 does not need to send an address
>   * @addr.buswidth: number of IO lines used to transmit the address cycles
> + * @addr.dtr: whether the address should be sent in DTR mode or not
>   * @addr.val: address value. This value is always sent MSB first on the bus.
>   *	      Note that only @addr.nbytes are taken into account in this
>   *	      address value, so users should make sure the value fits in the
>   *	      assigned number of bytes.
> - * @addr.dtr: whether the address should be sent in DTR mode or not
>   * @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can
>   *		  be zero if the operation does not require dummy bytes
>   * @dummy.buswidth: number of IO lanes used to transmit the dummy bytes
>   * @dummy.dtr: whether the dummy bytes should be sent in DTR mode or not
>   * @data.buswidth: number of IO lanes used to send/receive the data
>   * @data.dtr: whether the data should be sent in DTR mode or not
> + * @data.ecc: whether error correction is required or not
> + * @data.swap16: whether the byte order of 16-bit words is swapped when read
> + *		 or written in Octal DTR mode compared to STR mode.
>   * @data.dir: direction of the transfer
> - * @data.buf.in: input buffer
> - * @data.buf.out: output buffer
> + * @data.nbytes: number of data bytes to send/receive. Can be zero if the
> + *		 operation does not involve transferring data
> + * @data.buf.in: input buffer (must be DMA-able)
> + * @data.buf.out: output buffer (must be DMA-able)
> + * @max_freq: frequency limitation wrt this operation. 0 means there is no
> + *	      specific constraint and the highest achievable frequency can be
> + *	      attempted.
>   */
>  struct spi_mem_op {
>  	struct {
>  		u8 nbytes;
>  		u8 buswidth;
>  		u8 dtr : 1;
> +		u8 __pad : 7;
>  		u16 opcode;
>  	} cmd;
>  
> @@ -107,6 +160,7 @@ struct spi_mem_op {
>  		u8 nbytes;
>  		u8 buswidth;
>  		u8 dtr : 1;
> +		u8 __pad : 7;
>  		u64 val;
>  	} addr;
>  
> @@ -114,28 +168,35 @@ struct spi_mem_op {
>  		u8 nbytes;
>  		u8 buswidth;
>  		u8 dtr : 1;
> +		u8 __pad : 7;
>  	} dummy;
>  
>  	struct {
>  		u8 buswidth;
>  		u8 dtr : 1;
> +		u8 ecc : 1;
> +		u8 swap16 : 1;
> +		u8 __pad : 5;
>  		enum spi_mem_data_dir dir;
>  		unsigned int nbytes;
> -		/* buf.{in,out} must be DMA-able. */
>  		union {
>  			void *in;
>  			const void *out;
>  		} buf;
>  	} data;
> +
> +	unsigned int max_freq;
>  };
>  
> -#define SPI_MEM_OP(__cmd, __addr, __dummy, __data)		\
> +#define SPI_MEM_OP(__cmd, __addr, __dummy, __data, ...)		\
>  	{							\
>  		.cmd = __cmd,					\
>  		.addr = __addr,					\
>  		.dummy = __dummy,				\
>  		.data = __data,					\
> +		__VA_ARGS__					\
>  	}
> +
>  /**
>   * struct spi_mem_dirmap_info - Direct mapping information
>   * @op_tmpl: operation template that should be used by the direct mapping when
> @@ -143,7 +204,7 @@ struct spi_mem_op {
>   * @offset: absolute offset this direct mapping is pointing to
>   * @length: length in byte of this direct mapping
>   *
> - * This information is used by the controller specific implementation to know
> + * These information are used by the controller specific implementation to know
>   * the portion of memory that is directly mapped and the spi_mem_op that should
>   * be used to access the device.
>   * A direct mapping is only valid for one direction (read or write) and this
> @@ -223,10 +284,12 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
>  /**
>   * struct spi_controller_mem_ops - SPI memory operations
>   * @adjust_op_size: shrink the data xfer of an operation to match controller's
> - *		    limitations (can be alignment of max RX/TX size
> + *		    limitations (can be alignment or max RX/TX size
>   *		    limitations)
>   * @supports_op: check if an operation is supported by the controller
>   * @exec_op: execute a SPI memory operation
> + *           not all driver provides supports_op(), so it can return -EOPNOTSUPP
> + *           if the op is not supported by the driver/controller
>   * @dirmap_create: create a direct mapping descriptor that can later be used to
>   *		   access the memory device. This method is optional
>   * @dirmap_destroy: destroy a memory descriptor previous created by
> @@ -300,13 +363,16 @@ int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
>  void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
>  					  const struct spi_mem_op *op,
>  					  struct sg_table *sg);
> +
> +bool spi_mem_default_supports_op(struct spi_mem *mem,
> +				 const struct spi_mem_op *op);
>  #else
>  static inline int
>  spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
>  				   const struct spi_mem_op *op,
>  				   struct sg_table *sg)
>  {
> -	return -ENOSYS;
> +	return -ENOTSUPP;
>  }
>  
>  static inline void
> @@ -315,6 +381,13 @@ spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
>  				     struct sg_table *sg)
>  {
>  }
> +
> +static inline
> +bool spi_mem_default_supports_op(struct spi_mem *mem,
> +				 const struct spi_mem_op *op)
> +{
> +	return false;
> +}
>  #endif /* CONFIG_SPI_MEM */
>  #endif /* __UBOOT__ */
>  
> @@ -337,7 +410,6 @@ ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
>  			    u64 offs, size_t len, void *buf);
>  ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
>  			     u64 offs, size_t len, const void *buf);
> -
>  #ifndef __UBOOT__
>  int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
>  				       struct module *owner);



More information about the U-Boot mailing list