All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 00/13] spi: dw: Add support for XIP mode
@ 2021-02-05  4:39 Sean Anderson
  2021-02-05  4:39 ` [RFC PATCH 01/13] linux err: Synchronize with Linux 5.10 Sean Anderson
                   ` (13 more replies)
  0 siblings, 14 replies; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

This adds support for memory-mapped ("DIRMAP") reads (called XIP by the
datasheet). In theory, these have better performance than regular reads. In
practice, the CPU is already fast enough to max out the performance of this
peripheral.

The real end-goal is to be able to boot a XIP kernel. This SoC has just barely
enough SRAM to boot Linux, and using a XIP kernel would decrease the memory
pressure to something tolerable. It would also probably absolutely *tank* the
performance, since not only is SPI flash ~8x slower than regular memory, there
is no cache to mitigate that latency. So it remains to be seen whether this is
worth it at all.

This series is RFC because it currently reads all 1s on one board I have. On the
other board I have, it reads and writes fine. I will try and investigate this,
but I expect any changes to occur in "spi: dw: Add support for DIRMAP".


Sean Anderson (13):
  linux err: Synchronize with Linux 5.10
  spi-mem: Add dirmap API from Linux
  mtd: spi-nor: use spi-mem dirmap API
  core: ofnode: Fix inconsistent returns of *_read_u32_array
  mux: Inline mux functions when CONFIG_MUX is disabled
  mux: Define a stub for mux_get_by_index if CONFIG_MUX is disabled
  mux: mmio: Only complain about idle-states if it is malformed
  spi: dw: Define XIP registers
  spi: dw: Add XIP and XIP_CONCURRENT caps
  spi: dw: Use a mux to access registers
  spi: dw: Add support for DIRMAP
  riscv: k210: Increase SPI3 bus clock to CPU speed
  riscv: k210: Add bindings for SPI XIP

 arch/riscv/dts/k210.dtsi       |  25 ++-
 drivers/core/ofnode.c          |  15 +-
 drivers/mtd/spi/sf_probe.c     |  79 ++++++++
 drivers/mtd/spi/spi-nor-core.c |  45 +++--
 drivers/mux/mmio.c             |   5 +-
 drivers/spi/Kconfig            |  10 +
 drivers/spi/designware_spi.c   | 337 +++++++++++++++++++++++++++++++--
 drivers/spi/spi-mem.c          | 270 ++++++++++++++++++++++++++
 include/linux/err.h            |  21 +-
 include/linux/mtd/spi-nor.h    |   6 +
 include/mux.h                  |  25 ++-
 include/spi-mem.h              |  99 ++++++++++
 12 files changed, 889 insertions(+), 48 deletions(-)

-- 
2.29.2

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

* [RFC PATCH 01/13] linux err: Synchronize with Linux 5.10
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-07 14:37   ` Simon Glass
  2021-02-05  4:39 ` [RFC PATCH 02/13] spi-mem: Add dirmap API from Linux Sean Anderson
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

This synchronizes linux/err.h with Linux 5.10. Notably, this adds
PTR_ERR_OR_ZERO.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 include/linux/err.h | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/include/linux/err.h b/include/linux/err.h
index 5ede82432d..06a80f9f29 100644
--- a/include/linux/err.h
+++ b/include/linux/err.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _LINUX_ERR_H
 #define _LINUX_ERR_H
 
@@ -9,7 +10,7 @@
 
 /*
  * Kernel pointers have redundant information, so we can use a
- * scheme where we can return either an error code or a dentry
+ * scheme where we can return either an error code or a normal
  * pointer with the same return value.
  *
  * This should be a per-architecture thing, to allow different
@@ -21,24 +22,24 @@
 
 #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
 
-static inline void *ERR_PTR(long error)
+static inline void * __must_check ERR_PTR(long error)
 {
 	return (void *)(CONFIG_ERR_PTR_OFFSET + error);
 }
 
-static inline long PTR_ERR(const void *ptr)
+static inline long __must_check PTR_ERR(__force const void *ptr)
 {
 	return ((long)ptr - CONFIG_ERR_PTR_OFFSET);
 }
 
-static inline long IS_ERR(const void *ptr)
+static inline bool __must_check IS_ERR(__force const void *ptr)
 {
 	return IS_ERR_VALUE((unsigned long)PTR_ERR(ptr));
 }
 
-static inline bool IS_ERR_OR_NULL(const void *ptr)
+static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr)
 {
-	return !ptr || IS_ERR_VALUE((unsigned long)PTR_ERR(ptr));
+	return unlikely(!ptr) || IS_ERR_VALUE((unsigned long)ptr);
 }
 
 /**
@@ -54,6 +55,14 @@ static inline void * __must_check ERR_CAST(__force const void *ptr)
 	return (void *) ptr;
 }
 
+static inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr)
+{
+	if (IS_ERR(ptr))
+		return PTR_ERR(ptr);
+	else
+		return 0;
+}
+
 #endif
 
 #endif /* _LINUX_ERR_H */
-- 
2.29.2

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

* [RFC PATCH 02/13] spi-mem: Add dirmap API from Linux
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
  2021-02-05  4:39 ` [RFC PATCH 01/13] linux err: Synchronize with Linux 5.10 Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-05  4:39 ` [RFC PATCH 03/13] mtd: spi-nor: use spi-mem dirmap API Sean Anderson
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

This adds the dirmap API originally introduced in Linux commit aa167f3fed0c
("spi: spi-mem: Add a new API to support direct mapping"). This also
includes several follow-up patches and fixes.

Changes from Linux include:
* Added Kconfig option
* Changed struct device to struct udevice
* Changed struct spi_mem to struct spi_slave

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 drivers/spi/Kconfig   |  10 ++
 drivers/spi/spi-mem.c | 270 ++++++++++++++++++++++++++++++++++++++++++
 include/spi-mem.h     |  99 ++++++++++++++++
 3 files changed, 379 insertions(+)

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 1494c91763..9663b20c74 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -40,6 +40,16 @@ config SPI_MEM
 	  This extension is meant to simplify interaction with SPI memories
 	  by providing an high-level interface to send memory-like commands.
 
+config SPI_DIRMAP
+	bool "SPI direct mapping"
+	depends on SPI_MEM
+	help
+	  Enable the SPI direct mapping API. Most modern SPI controllers can
+	  directly map a SPI memory (or a portion of the SPI memory) in the CPU
+	  address space. Most of the time this brings significant performance
+	  improvements as it automates the whole process of sending SPI memory
+	  operations every time a new region is accessed.
+
 if DM_SPI
 
 config ALTERA_SPI
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 6772367ef7..597cd63c00 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -21,6 +21,8 @@
 #include <spi.h>
 #include <spi-mem.h>
 #include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <linux/bug.h>
 #endif
 
 #ifndef __UBOOT__
@@ -468,6 +470,274 @@ int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)
 }
 EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
 
+#if CONFIG_IS_ENABLED(SPI_DIRMAP)
+static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
+				      u64 offs, size_t len, void *buf)
+{
+	struct spi_mem_op op = desc->info.op_tmpl;
+	int ret;
+
+	op.addr.val = desc->info.offset + offs;
+	op.data.buf.in = buf;
+	op.data.nbytes = len;
+	ret = spi_mem_adjust_op_size(desc->slave, &op);
+	if (ret)
+		return ret;
+
+	ret = spi_mem_exec_op(desc->slave, &op);
+	if (ret)
+		return ret;
+
+	return op.data.nbytes;
+}
+
+static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc,
+				       u64 offs, size_t len, const void *buf)
+{
+	struct spi_mem_op op = desc->info.op_tmpl;
+	int ret;
+
+	op.addr.val = desc->info.offset + offs;
+	op.data.buf.out = buf;
+	op.data.nbytes = len;
+	ret = spi_mem_adjust_op_size(desc->slave, &op);
+	if (ret)
+		return ret;
+
+	ret = spi_mem_exec_op(desc->slave, &op);
+	if (ret)
+		return ret;
+
+	return op.data.nbytes;
+}
+
+/**
+ * spi_mem_dirmap_create() - Create a direct mapping descriptor
+ * @mem: SPI mem device this direct mapping should be created for
+ * @info: direct mapping information
+ *
+ * This function is creating a direct mapping descriptor which can then be used
+ * to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write().
+ * If the SPI controller driver does not support direct mapping, this function
+ * falls back to an implementation using spi_mem_exec_op(), so that the caller
+ * doesn't have to bother implementing a fallback on his own.
+ *
+ * Return: a valid pointer in case of success, and ERR_PTR() otherwise.
+ */
+struct spi_mem_dirmap_desc *
+spi_mem_dirmap_create(struct spi_slave *slave,
+		      const struct spi_mem_dirmap_info *info)
+{
+	struct udevice *bus = slave->dev->parent;
+	struct dm_spi_ops *ops = spi_get_ops(bus);
+	struct spi_mem_dirmap_desc *desc;
+	int ret = -ENOTSUPP;
+
+	/* Make sure the number of address cycles is between 1 and 8 bytes. */
+	if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8)
+		return ERR_PTR(-EINVAL);
+
+	/* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */
+	if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA)
+		return ERR_PTR(-EINVAL);
+
+	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return ERR_PTR(-ENOMEM);
+
+	desc->slave = slave;
+	desc->info = *info;
+	if (ops->mem_ops && ops->mem_ops->dirmap_create)
+		ret = ops->mem_ops->dirmap_create(desc);
+
+	if (ret) {
+		desc->nodirmap = true;
+		if (!spi_mem_supports_op(desc->slave, &desc->info.op_tmpl))
+			ret = -ENOTSUPP;
+		else
+			ret = 0;
+	}
+
+	if (ret) {
+		kfree(desc);
+		return ERR_PTR(ret);
+	}
+
+	return desc;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_create);
+
+/**
+ * spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor
+ * @desc: the direct mapping descriptor to destroy
+ *
+ * This function destroys a direct mapping descriptor previously created by
+ * spi_mem_dirmap_create().
+ */
+void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc)
+{
+	struct udevice *bus = desc->slave->dev->parent;
+	struct dm_spi_ops *ops = spi_get_ops(bus);
+
+	if (!desc->nodirmap && ops->mem_ops && ops->mem_ops->dirmap_destroy)
+		ops->mem_ops->dirmap_destroy(desc);
+
+	kfree(desc);
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_destroy);
+
+#ifndef __UBOOT__
+static void devm_spi_mem_dirmap_release(struct udevice *dev, void *res)
+{
+	struct spi_mem_dirmap_desc *desc = *(struct spi_mem_dirmap_desc **)res;
+
+	spi_mem_dirmap_destroy(desc);
+}
+
+/**
+ * devm_spi_mem_dirmap_create() - Create a direct mapping descriptor and attach
+ *				  it to a device
+ * @dev: device the dirmap desc will be attached to
+ * @mem: SPI mem device this direct mapping should be created for
+ * @info: direct mapping information
+ *
+ * devm_ variant of the spi_mem_dirmap_create() function. See
+ * spi_mem_dirmap_create() for more details.
+ *
+ * Return: a valid pointer in case of success, and ERR_PTR() otherwise.
+ */
+struct spi_mem_dirmap_desc *
+devm_spi_mem_dirmap_create(struct udevice *dev, struct spi_slave *slave,
+			   const struct spi_mem_dirmap_info *info)
+{
+	struct spi_mem_dirmap_desc **ptr, *desc;
+
+	ptr = devres_alloc(devm_spi_mem_dirmap_release, sizeof(*ptr),
+			   GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	desc = spi_mem_dirmap_create(slave, info);
+	if (IS_ERR(desc)) {
+		devres_free(ptr);
+	} else {
+		*ptr = desc;
+		devres_add(dev, ptr);
+	}
+
+	return desc;
+}
+EXPORT_SYMBOL_GPL(devm_spi_mem_dirmap_create);
+
+static int devm_spi_mem_dirmap_match(struct udevice *dev, void *res, void *data)
+{
+	struct spi_mem_dirmap_desc **ptr = res;
+
+	if (WARN_ON(!ptr || !*ptr))
+		return 0;
+
+	return *ptr == data;
+}
+
+/**
+ * devm_spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor attached
+ *				   to a device
+ * @dev: device the dirmap desc is attached to
+ * @desc: the direct mapping descriptor to destroy
+ *
+ * devm_ variant of the spi_mem_dirmap_destroy() function. See
+ * spi_mem_dirmap_destroy() for more details.
+ */
+void devm_spi_mem_dirmap_destroy(struct udevice *dev,
+				 struct spi_mem_dirmap_desc *desc)
+{
+	devres_release(dev, devm_spi_mem_dirmap_release,
+		       devm_spi_mem_dirmap_match, desc);
+}
+EXPORT_SYMBOL_GPL(devm_spi_mem_dirmap_destroy);
+#endif /* __UBOOT__ */
+
+/**
+ * spi_mem_dirmap_read() - Read data through a direct mapping
+ * @desc: direct mapping descriptor
+ * @offs: offset to start reading from. Note that this is not an absolute
+ *	  offset, but the offset within the direct mapping which already has
+ *	  its own offset
+ * @len: length in bytes
+ * @buf: destination buffer. This buffer must be DMA-able
+ *
+ * This function reads data from a memory device using a direct mapping
+ * previously instantiated with spi_mem_dirmap_create().
+ *
+ * Return: the amount of data read from the memory device or a negative error
+ * code. Note that the returned size might be smaller than @len, and the caller
+ * is responsible for calling spi_mem_dirmap_read() again when that happens.
+ */
+ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
+			    u64 offs, size_t len, void *buf)
+{
+	struct udevice *bus = desc->slave->dev->parent;
+	struct dm_spi_ops *ops = spi_get_ops(bus);
+	ssize_t ret;
+
+	if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
+		return -EINVAL;
+
+	if (!len)
+		return 0;
+
+	if (desc->nodirmap)
+		ret = spi_mem_no_dirmap_read(desc, offs, len, buf);
+	else if (ops->mem_ops && ops->mem_ops->dirmap_read)
+		ret = ops->mem_ops->dirmap_read(desc, offs, len, buf);
+	else
+		ret = -ENOTSUPP;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_read);
+
+/**
+ * spi_mem_dirmap_write() - Write data through a direct mapping
+ * @desc: direct mapping descriptor
+ * @offs: offset to start writing from. Note that this is not an absolute
+ *	  offset, but the offset within the direct mapping which already has
+ *	  its own offset
+ * @len: length in bytes
+ * @buf: source buffer. This buffer must be DMA-able
+ *
+ * This function writes data to a memory device using a direct mapping
+ * previously instantiated with spi_mem_dirmap_create().
+ *
+ * Return: the amount of data written to the memory device or a negative error
+ * code. Note that the returned size might be smaller than @len, and the caller
+ * is responsible for calling spi_mem_dirmap_write() again when that happens.
+ */
+ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
+			     u64 offs, size_t len, const void *buf)
+{
+	struct udevice *bus = desc->slave->dev->parent;
+	struct dm_spi_ops *ops = spi_get_ops(bus);
+	ssize_t ret;
+
+	if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT)
+		return -EINVAL;
+
+	if (!len)
+		return 0;
+
+	if (desc->nodirmap)
+		ret = spi_mem_no_dirmap_write(desc, offs, len, buf);
+	else if (ops->mem_ops && ops->mem_ops->dirmap_write)
+		ret = ops->mem_ops->dirmap_write(desc, offs, len, buf);
+	else
+		ret = -ENOTSUPP;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_write);
+#endif /* CONFIG_SPI_DIRMAP */
+
 #ifndef __UBOOT__
 static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
 {
diff --git a/include/spi-mem.h b/include/spi-mem.h
index 1dd556b6cf..d0a247cffc 100644
--- a/include/spi-mem.h
+++ b/include/spi-mem.h
@@ -123,6 +123,49 @@ struct spi_mem_op {
 		.data = __data,					\
 	}
 
+/**
+ * struct spi_mem_dirmap_info - Direct mapping information
+ * @op_tmpl: operation template that should be used by the direct mapping when
+ *	     the memory device is accessed
+ * @offset: absolute offset this direct mapping is pointing to
+ * @length: length in byte of this direct mapping
+ *
+ * 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
+ * direction is directly encoded in the ->op_tmpl.data.dir field.
+ */
+struct spi_mem_dirmap_info {
+	struct spi_mem_op op_tmpl;
+	u64 offset;
+	u64 length;
+};
+
+/**
+ * struct spi_mem_dirmap_desc - Direct mapping descriptor
+ * @mem: the SPI memory device this direct mapping is attached to
+ * @info: information passed at direct mapping creation time
+ * @nodirmap: set to 1 if the SPI controller does not implement
+ *	      ->mem_ops->dirmap_create() or when this function returned an
+ *	      error. If @nodirmap is true, all spi_mem_dirmap_{read,write}()
+ *	      calls will use spi_mem_exec_op() to access the memory. This is a
+ *	      degraded mode that allows spi_mem drivers to use the same code
+ *	      no matter whether the controller supports direct mapping or not
+ * @priv: field pointing to controller specific data
+ *
+ * Common part of a direct mapping descriptor. This object is created by
+ * spi_mem_dirmap_create() and controller implementation of ->create_dirmap()
+ * can create/attach direct mapping resources to the descriptor in the ->priv
+ * field.
+ */
+struct spi_mem_dirmap_desc {
+	struct spi_slave *slave;
+	struct spi_mem_dirmap_info info;
+	unsigned int nodirmap;
+	void *priv;
+};
+
 #ifndef __UBOOT__
 /**
  * struct spi_mem - describes a SPI memory device
@@ -171,10 +214,32 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
  *		    limitations)
  * @supports_op: check if an operation is supported by the controller
  * @exec_op: execute a SPI memory operation
+ * @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
+ *		    ->dirmap_create()
+ * @dirmap_read: read data from the memory device using the direct mapping
+ *		 created by ->dirmap_create(). The function can return less
+ *		 data than requested (for example when the request is crossing
+ *		 the currently mapped area), and the caller of
+ *		 spi_mem_dirmap_read() is responsible for calling it again in
+ *		 this case.
+ * @dirmap_write: write data to the memory device using the direct mapping
+ *		  created by ->dirmap_create(). The function can return less
+ *		  data than requested (for example when the request is crossing
+ *		  the currently mapped area), and the caller of
+ *		  spi_mem_dirmap_write() is responsible for calling it again in
+ *		  this case.
  *
  * This interface should be implemented by SPI controllers providing an
  * high-level interface to execute SPI memory operation, which is usually the
  * case for QSPI controllers.
+ *
+ * Note on ->dirmap_{read,write}(): drivers should avoid accessing the direct
+ * mapping from the CPU because doing that can stall the CPU waiting for the
+ * SPI mem transaction to finish, and this will make real-time maintainers
+ * unhappy and might make your system less reactive. Instead, drivers should
+ * use DMA to access this direct mapping.
  */
 struct spi_controller_mem_ops {
 	int (*adjust_op_size)(struct spi_slave *slave, struct spi_mem_op *op);
@@ -182,6 +247,12 @@ struct spi_controller_mem_ops {
 			    const struct spi_mem_op *op);
 	int (*exec_op)(struct spi_slave *slave,
 		       const struct spi_mem_op *op);
+	int (*dirmap_create)(struct spi_mem_dirmap_desc *desc);
+	void (*dirmap_destroy)(struct spi_mem_dirmap_desc *desc);
+	ssize_t (*dirmap_read)(struct spi_mem_dirmap_desc *desc,
+			       u64 offs, size_t len, void *buf);
+	ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc,
+				u64 offs, size_t len, const void *buf);
 };
 
 #ifndef __UBOOT__
@@ -243,6 +314,34 @@ bool spi_mem_supports_op(struct spi_slave *slave, const struct spi_mem_op *op);
 
 int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op);
 
+#if CONFIG_IS_ENABLED(SPI_DIRMAP)
+struct spi_mem_dirmap_desc *
+spi_mem_dirmap_create(struct spi_slave *mem,
+		      const struct spi_mem_dirmap_info *info);
+void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc);
+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);
+#else
+static inline void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc)
+{
+}
+
+static inline ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
+					  u64 offs, size_t len, void *buf)
+{
+	return -ENOTSUPP;
+}
+
+static inline ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
+					   u64 offs, size_t len,
+					   const void *buf)
+{
+	return -ENOTSUPP;
+}
+#endif /* CONFIG_SPI_DIRMAP */
+
 #ifndef __UBOOT__
 int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
 				       struct module *owner);
-- 
2.29.2

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

* [RFC PATCH 03/13] mtd: spi-nor: use spi-mem dirmap API
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
  2021-02-05  4:39 ` [RFC PATCH 01/13] linux err: Synchronize with Linux 5.10 Sean Anderson
  2021-02-05  4:39 ` [RFC PATCH 02/13] spi-mem: Add dirmap API from Linux Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-05  4:39 ` [RFC PATCH 04/13] core: ofnode: Fix inconsistent returns of *_read_u32_array Sean Anderson
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

This adds support for the dirmap API to the spi-nor subsystem, as
introduced in Linux commit df5c21002cf4 ("mtd: spi-nor: use spi-mem dirmap
API").

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 drivers/mtd/spi/sf_probe.c     | 79 ++++++++++++++++++++++++++++++++++
 drivers/mtd/spi/spi-nor-core.c | 45 +++++++++++++------
 include/linux/mtd/spi-nor.h    |  6 +++
 3 files changed, 116 insertions(+), 14 deletions(-)

diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c
index 6c87434867..796a2fa6bc 100644
--- a/drivers/mtd/spi/sf_probe.c
+++ b/drivers/mtd/spi/sf_probe.c
@@ -14,9 +14,72 @@
 #include <malloc.h>
 #include <spi.h>
 #include <spi_flash.h>
+#include <spi-mem.h>
 
 #include "sf_internal.h"
 
+#if CONFIG_IS_ENABLED(SPI_DIRMAP)
+static int spi_nor_create_read_dirmap(struct spi_nor *nor)
+{
+	struct spi_mem_dirmap_info info = {
+		.op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
+				      SPI_MEM_OP_ADDR(nor->addr_width, 0, 1),
+				      SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
+				      SPI_MEM_OP_DATA_IN(0, NULL, 1)),
+		.offset = 0,
+		.length = nor->mtd.size,
+	};
+	struct spi_mem_op *op = &info.op_tmpl;
+
+	/* get transfer protocols. */
+	op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
+	op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
+	op->dummy.buswidth = op->addr.buswidth;
+	op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
+
+	/* convert the dummy cycles to the number of bytes */
+	op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8;
+
+	nor->dirmap.rdesc = spi_mem_dirmap_create(nor->spi, &info);
+	return PTR_ERR_OR_ZERO(nor->dirmap.rdesc);
+}
+
+static int spi_nor_create_write_dirmap(struct spi_nor *nor)
+{
+	struct spi_mem_dirmap_info info = {
+		.op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
+				      SPI_MEM_OP_ADDR(nor->addr_width, 0, 1),
+				      SPI_MEM_OP_NO_DUMMY,
+				      SPI_MEM_OP_DATA_OUT(0, NULL, 1)),
+		.offset = 0,
+		.length = nor->mtd.size,
+	};
+	struct spi_mem_op *op = &info.op_tmpl;
+
+	/* get transfer protocols. */
+	op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
+	op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
+	op->dummy.buswidth = op->addr.buswidth;
+	op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
+
+	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
+		op->addr.nbytes = 0;
+
+	nor->dirmap.wdesc = spi_mem_dirmap_create(nor->spi, &info);
+	return PTR_ERR_OR_ZERO(nor->dirmap.wdesc);
+}
+#else
+static int spi_nor_create_read_dirmap(struct spi_nor *nor)
+{
+	return 0;
+}
+
+static int spi_nor_create_write_dirmap(struct spi_nor *nor)
+{
+	return 0;
+}
+#endif /* CONFIG_SPI_DIRMAP */
+
 /**
  * spi_flash_probe_slave() - Probe for a SPI flash device on a bus
  *
@@ -45,6 +108,14 @@ static int spi_flash_probe_slave(struct spi_flash *flash)
 	if (ret)
 		goto err_read_id;
 
+	ret = spi_nor_create_read_dirmap(flash);
+	if (ret)
+		return ret;
+
+	ret = spi_nor_create_write_dirmap(flash);
+	if (ret)
+		return ret;
+
 	if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
 		ret = spi_flash_mtd_register(flash);
 
@@ -83,6 +154,9 @@ struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs,
 
 void spi_flash_free(struct spi_flash *flash)
 {
+	spi_mem_dirmap_destroy(flash->dirmap.wdesc);
+	spi_mem_dirmap_destroy(flash->dirmap.rdesc);
+
 	if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
 		spi_flash_mtd_unregister();
 
@@ -143,6 +217,11 @@ int spi_flash_std_probe(struct udevice *dev)
 
 static int spi_flash_std_remove(struct udevice *dev)
 {
+	struct spi_flash *flash = dev_get_uclass_priv(dev);
+
+	spi_mem_dirmap_destroy(flash->dirmap.wdesc);
+	spi_mem_dirmap_destroy(flash->dirmap.rdesc);
+
 	if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
 		spi_flash_mtd_unregister();
 
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index e16b0e1462..32c712a9f1 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -96,13 +96,23 @@ static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
 
 	while (remaining) {
 		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
-		ret = spi_mem_adjust_op_size(nor->spi, &op);
-		if (ret)
-			return ret;
 
-		ret = spi_mem_exec_op(nor->spi, &op);
-		if (ret)
-			return ret;
+		if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.rdesc) {
+			ret = spi_mem_dirmap_read(nor->dirmap.rdesc,
+						  op.addr.val, op.data.nbytes,
+						  op.data.buf.in);
+			if (ret < 0)
+				return ret;
+			op.data.nbytes = ret;
+		} else {
+			ret = spi_mem_adjust_op_size(nor->spi, &op);
+			if (ret)
+				return ret;
+
+			ret = spi_mem_exec_op(nor->spi, &op);
+			if (ret)
+				return ret;
+		}
 
 		op.addr.val += op.data.nbytes;
 		remaining -= op.data.nbytes;
@@ -120,6 +130,7 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
 				   SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
 				   SPI_MEM_OP_NO_DUMMY,
 				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
+	ssize_t nbytes;
 	int ret;
 
 	/* get transfer protocols. */
@@ -130,16 +141,22 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
 	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
 		op.addr.nbytes = 0;
 
-	ret = spi_mem_adjust_op_size(nor->spi, &op);
-	if (ret)
-		return ret;
-	op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
+	if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.wdesc) {
+		nbytes = spi_mem_dirmap_write(nor->dirmap.wdesc, op.addr.val,
+					      op.data.nbytes, op.data.buf.out);
+	} else {
+		ret = spi_mem_adjust_op_size(nor->spi, &op);
+		if (ret)
+			return ret;
+		op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
 
-	ret = spi_mem_exec_op(nor->spi, &op);
-	if (ret)
-		return ret;
+		ret = spi_mem_exec_op(nor->spi, &op);
+		if (ret)
+			return ret;
+		nbytes = op.data.nbytes;
+	}
 
-	return op.data.nbytes;
+	return nbytes;
 }
 
 /*
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 39ef872ebf..ffa4dd3e7d 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -304,6 +304,7 @@ struct spi_flash {
  * @flash_is_locked:	[FLASH-SPECIFIC] check if a region of the SPI NOR is
  *			completely locked
  * @quad_enable:	[FLASH-SPECIFIC] enables SPI NOR quad mode
+ * @dirmap:             pointers to struct spi_mem_dirmap_desc for reads/writes.
  * @priv:		the private data
  */
 struct spi_nor {
@@ -346,6 +347,11 @@ struct spi_nor {
 	int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
 	int (*quad_enable)(struct spi_nor *nor);
 
+	struct {
+		struct spi_mem_dirmap_desc *rdesc;
+		struct spi_mem_dirmap_desc *wdesc;
+	} dirmap;
+
 	void *priv;
 /* Compatibility for spi_flash, remove once sf layer is merged with mtd */
 	const char *name;
-- 
2.29.2

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

* [RFC PATCH 04/13] core: ofnode: Fix inconsistent returns of *_read_u32_array
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
                   ` (2 preceding siblings ...)
  2021-02-05  4:39 ` [RFC PATCH 03/13] mtd: spi-nor: use spi-mem dirmap API Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-07 14:37   ` Simon Glass
  2021-02-05  4:39 ` [RFC PATCH 05/13] mux: Inline mux functions when CONFIG_MUX is disabled Sean Anderson
                   ` (9 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

The documentation for dev_read_u32_array says the return value is an errno,
but fdtdec_get_int_array returns FDT_ERRs. Convert the return values so
callers can handle errors properly.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 drivers/core/ofnode.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index 7a5f4c0a73..c071b968d5 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -220,9 +220,18 @@ int ofnode_read_u32_array(ofnode node, const char *propname,
 		return of_read_u32_array(ofnode_to_np(node), propname,
 					 out_values, sz);
 	} else {
-		return fdtdec_get_int_array(gd->fdt_blob,
-					    ofnode_to_offset(node), propname,
-					    out_values, sz);
+		int err = fdtdec_get_int_array(gd->fdt_blob,
+					       ofnode_to_offset(node), propname,
+					       out_values, sz);
+
+		switch (err) {
+		case FDT_ERR_NOTFOUND:
+			return -EINVAL;
+		case FDT_ERR_BADLAYOUT:
+			return -EOVERFLOW;
+		default:
+			return 0;
+		}
 	}
 }
 
-- 
2.29.2

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

* [RFC PATCH 05/13] mux: Inline mux functions when CONFIG_MUX is disabled
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
                   ` (3 preceding siblings ...)
  2021-02-05  4:39 ` [RFC PATCH 04/13] core: ofnode: Fix inconsistent returns of *_read_u32_array Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-05  7:32   ` Pratyush Yadav
  2021-02-05  4:39 ` [RFC PATCH 06/13] mux: Define a stub for mux_get_by_index if " Sean Anderson
                   ` (8 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

This prevents multiple-definition errors.

Fixes: 0ad40b2463 ("drivers: Add a new framework for multiplexer devices")

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 include/mux.h | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/include/mux.h b/include/mux.h
index 23844f480a..91a89bbb9a 100644
--- a/include/mux.h
+++ b/include/mux.h
@@ -117,40 +117,41 @@ struct mux_control *devm_mux_control_get(struct udevice *dev,
 int dm_mux_init(void);
 
 #else
-unsigned int mux_control_states(struct mux_control *mux)
+static inline unsigned int mux_control_states(struct mux_control *mux)
 {
 	return -ENOSYS;
 }
 
-int __must_check mux_control_select(struct mux_control *mux,
-				    unsigned int state)
+static inline int __must_check mux_control_select(struct mux_control *mux,
+						  unsigned int state)
 {
 	return -ENOSYS;
 }
 
 #define mux_control_try_select(mux) mux_control_select(mux)
 
-int mux_control_deselect(struct mux_control *mux)
+static inline int mux_control_deselect(struct mux_control *mux)
 {
 	return -ENOSYS;
 }
 
-struct mux_control *mux_control_get(struct udevice *dev, const char *mux_name)
+static inline struct mux_control *mux_control_get(struct udevice *dev,
+						  const char *mux_name)
 {
 	return NULL;
 }
 
-void mux_control_put(struct mux_control *mux)
+static inline void mux_control_put(struct mux_control *mux)
 {
 }
 
-struct mux_control *devm_mux_control_get(struct udevice *dev,
-					 const char *mux_name)
+static inline struct mux_control *devm_mux_control_get(struct udevice *dev,
+						       const char *mux_name)
 {
 	return NULL;
 }
 
-int dm_mux_init(void)
+static inline int dm_mux_init(void)
 {
 	return -ENOSYS;
 }
-- 
2.29.2

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

* [RFC PATCH 06/13] mux: Define a stub for mux_get_by_index if CONFIG_MUX is disabled
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
                   ` (4 preceding siblings ...)
  2021-02-05  4:39 ` [RFC PATCH 05/13] mux: Inline mux functions when CONFIG_MUX is disabled Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-05 10:53   ` Pratyush Yadav
  2021-02-05  4:39 ` [RFC PATCH 07/13] mux: mmio: Only complain about idle-states if it is malformed Sean Anderson
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

This allows code using mux_get_by_index to be agnostic about CONFIG_MUX.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 include/mux.h | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/include/mux.h b/include/mux.h
index 91a89bbb9a..882a00068b 100644
--- a/include/mux.h
+++ b/include/mux.h
@@ -135,6 +135,12 @@ static inline int mux_control_deselect(struct mux_control *mux)
 	return -ENOSYS;
 }
 
+static inline int mux_get_by_index(struct udevice *dev, int index,
+				   struct mux_control **mux)
+{
+	return -ENOSYS;
+}
+
 static inline struct mux_control *mux_control_get(struct udevice *dev,
 						  const char *mux_name)
 {
-- 
2.29.2

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

* [RFC PATCH 07/13] mux: mmio: Only complain about idle-states if it is malformed
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
                   ` (5 preceding siblings ...)
  2021-02-05  4:39 ` [RFC PATCH 06/13] mux: Define a stub for mux_get_by_index if " Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-05 11:06   ` Pratyush Yadav
  2021-02-05  4:39 ` [RFC PATCH 08/13] spi: dw: Define XIP registers Sean Anderson
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

idle-states is optional, so don't complain if it doesn't exist.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 drivers/mux/mmio.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c
index 00e0282dcc..82b1cc6aab 100644
--- a/drivers/mux/mmio.c
+++ b/drivers/mux/mmio.c
@@ -87,8 +87,11 @@ static int mmio_mux_probe(struct udevice *dev)
 
 	ret = dev_read_u32_array(dev, "idle-states", idle_states, num_fields);
 	if (ret < 0) {
-		log_err("idle-states");
 		devm_kfree(dev, idle_states);
+		/* dev_read_u32_array returns -EINVAL on missing property */
+		if (ret != -EINVAL)
+			return log_msg_ret("idle-states", -EINVAL);
+
 		idle_states = NULL;
 	}
 
-- 
2.29.2

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

* [RFC PATCH 08/13] spi: dw: Define XIP registers
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
                   ` (6 preceding siblings ...)
  2021-02-05  4:39 ` [RFC PATCH 07/13] mux: mmio: Only complain about idle-states if it is malformed Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-05  4:39 ` [RFC PATCH 09/13] spi: dw: Add XIP and XIP_CONCURRENT caps Sean Anderson
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

These registers and fields are necessary for XIP with
SSIC_CONCURRENT_XIP_EN.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 drivers/spi/designware_spi.c | 44 +++++++++++++++++++++++++++++++-----
 1 file changed, 38 insertions(+), 6 deletions(-)

diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index 44fb679fdb..d7510646e7 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -58,6 +58,13 @@
 #define DW_SPI_DR			0x60
 #define DW_SPI_RX_SAMPLE_DLY		0xf0
 #define DW_SPI_SPI_CTRL0		0xf4
+#define DW_SPI_XIP_MODE_BITS		0xfc
+#define DW_SPI_XIP_INCR_INST		0x100
+#define DW_SPI_XIP_WRAP_INST		0x104
+#define DW_SPI_XIP_CTRL			0x108
+#define DW_SPI_XIP_SER			0x10c
+#define DW_SPI_XRXOICR			0x110
+#define DW_SPI_XIP_XNT_TIME_OUT		0x114
 
 /* Bit fields in CTRLR0 */
 /*
@@ -147,9 +154,9 @@
  * FRF_BYTE
  */
 #define SPI_CTRLR0_TRANS_TYPE_MASK	GENMASK(1, 0)
-#define SPI_CTRLR0_TRANS_TYPE_1_1_X	0x0
-#define SPI_CTRLR0_TRANS_TYPE_1_X_X	0x1
-#define SPI_CTRLR0_TRANS_TYPE_X_X_X	0x2
+#define TRANS_TYPE_1_1_X		0x0
+#define TRANS_TYPE_1_X_X		0x1
+#define TRANS_TYPE_X_X_X		0x2
 /* Address length in 4-bit units */
 #define SPI_CTRLR0_ADDR_L_MASK		GENMASK(5, 2)
 /* Enable mode bits after address in XIP mode */
@@ -165,6 +172,31 @@
 /* Stretch the clock if the FIFO over/underflows */
 #define SPI_CTRLR0_CLK_STRETCH_EN	BIT(30)
 
+/* Bit fields in XIP_CTRL */
+
+/* XIP SPI frame format */
+#define XIP_CTRL_FRF			GENMASK(1, 0)
+/* Same as SPI_CTRLR0_TRANS_TYPE */
+#define XIP_CTRL_TRANS_TYPE_MASK	GENMASK(3, 2)
+/* Address length in 4-bit increments */
+#define XIP_CTRL_ADDR_L_MASK		GENMASK(7, 4)
+/* Same as SPI_CTRLR0_INST_L */
+#define XIP_CTRL_INST_L_MASK		GENMASK(10, 9)
+/* Enable mode bits */
+#define XIP_CTRL_MD_BITS_EN		BIT(12)
+/* Wait cycles */
+#define XIP_CTRL_WAIT_CYCLES_MASK	GENMASK(17, 13)
+/* Use fixed-size DFS in XIP mode, ignoring AHB request width */
+#define XIP_CTRL_DFS_HC			BIT(18)
+/* Enable instruction phase */
+#define XIP_CTRL_INST_EN		BIT(22)
+/* Continuous transfer: don't deselect slave after one transfer */
+#define XIP_CTRL_CONT_XFER_EN		BIT(23)
+/* Mode bits length; length = 1 << (MBL + 1) */
+#define XIP_CTRL_XIP_MBL_MASK		GENMASK(27, 26)
+/* Prefetch contiguous data frames */
+#define XIP_CTRL_PREFETCH_EN		BIT(29)
+
 #define RX_TIMEOUT			1000		/* timeout in ms */
 
 struct dw_spi_plat {
@@ -243,11 +275,11 @@ static u32 dw_spi_update_spi_cr0(const struct spi_mem_op *op)
 
 	/* This assumes support_op has filtered invalid types */
 	if (op->addr.buswidth == 1)
-		trans_type = SPI_CTRLR0_TRANS_TYPE_1_1_X;
+		trans_type = TRANS_TYPE_1_1_X;
 	else if (op->cmd.buswidth == 1)
-		trans_type = SPI_CTRLR0_TRANS_TYPE_1_X_X;
+		trans_type = TRANS_TYPE_1_X_X;
 	else
-		trans_type = SPI_CTRLR0_TRANS_TYPE_X_X_X;
+		trans_type = TRANS_TYPE_X_X_X;
 
 	if (op->dummy.buswidth)
 		wait_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth;
-- 
2.29.2

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

* [RFC PATCH 09/13] spi: dw: Add XIP and XIP_CONCURRENT caps
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
                   ` (7 preceding siblings ...)
  2021-02-05  4:39 ` [RFC PATCH 08/13] spi: dw: Define XIP registers Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-05  4:39 ` [RFC PATCH 10/13] spi: dw: Use a mux to access registers Sean Anderson
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

Both DW SSI APB and DWC SSI devices have an optional XIP mode. When the
xip_en signal is asserted, reads (and writes if SSIC_XIP_WRITE_REG_EN is
set) are mapped to SPI transfers.

If SSIC_CONCURRENT_XIP_EN is disabled, then XIP transfers are controlled
using SPI_CTRLR0. However, if SSIC_CONCURRENT_XIP_EN is enabled, then XIP
transfers can occur concurrently (first-come-first-serve) with non-XIP
transfers. To facilitate this, a separate XIP_CTRL register is used for
configuration which would otherwise by done using CTRLR0 and SPI_CTRLR0.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 drivers/spi/designware_spi.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index d7510646e7..c41c5b4982 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -202,6 +202,7 @@
 struct dw_spi_plat {
 	s32 frequency;		/* Default clock frequency, -1 for none */
 	void __iomem *regs;
+	fdt_size_t regs_size;
 };
 
 struct dw_spi_priv {
@@ -210,12 +211,15 @@ struct dw_spi_priv {
 	struct gpio_desc cs_gpio;	/* External chip-select gpio */
 
 	void __iomem *regs;
+	fdt_size_t regs_size;
 /* DW SPI capabilities */
 #define DW_SPI_CAP_CS_OVERRIDE		BIT(0) /* Unimplemented */
 #define DW_SPI_CAP_KEEMBAY_MST		BIT(1) /* Unimplemented */
 #define DW_SPI_CAP_DWC_SSI		BIT(2)
 #define DW_SPI_CAP_DFS32		BIT(3)
 #define DW_SPI_CAP_ENHANCED		BIT(4)
+#define DW_SPI_CAP_XIP			BIT(5)
+#define DW_SPI_CAP_XIP_CONCURRENT	BIT(6)
 	unsigned long caps;
 	unsigned long bus_clk_rate;
 	unsigned int freq;		/* Default frequency */
@@ -322,11 +326,13 @@ static int request_gpio_cs(struct udevice *bus)
 
 static int dw_spi_of_to_plat(struct udevice *bus)
 {
+	fdt_addr_t regs;
 	struct dw_spi_plat *plat = dev_get_plat(bus);
 
-	plat->regs = dev_read_addr_ptr(bus);
-	if (!plat->regs)
+	regs = dev_read_addr_size_index(bus, 0, &plat->regs_size);
+	if (regs == FDT_ADDR_T_NONE)
 		return -EINVAL;
+	plat->regs = (void *)regs;
 
 	/* Use 500KHz as a suitable default */
 	plat->frequency = dev_read_u32_default(bus, "spi-max-frequency",
@@ -375,6 +381,19 @@ static void spi_hw_init(struct udevice *bus, struct dw_spi_priv *priv)
 		priv->caps |= DW_SPI_CAP_ENHANCED;
 	}
 
+	/*
+	 * DWC_SPI always has this register with SSIC_XIP_EN. There is no way
+	 * to detect XIP for DW APB SSI
+	 */
+	dw_write(priv, DW_SPI_XIP_INCR_INST, 0xffffffff);
+	if (dw_read(priv, DW_SPI_XIP_INCR_INST))
+		priv->caps |= DW_SPI_CAP_XIP;
+
+	/* Exists with SSIC_CONCURRENT_XIP_EN */
+	dw_write(priv, DW_SPI_XIP_CTRL, 0xffffffff);
+	if (dw_read(priv, DW_SPI_XIP_CTRL))
+		priv->caps |= DW_SPI_CAP_XIP_CONCURRENT;
+
 	dw_write(priv, DW_SPI_SSIENR, 1);
 
 	/*
@@ -469,6 +488,7 @@ static int dw_spi_probe(struct udevice *bus)
 	u32 version;
 
 	priv->regs = plat->regs;
+	priv->regs_size = plat->regs_size;
 	priv->freq = plat->frequency;
 
 	ret = dw_spi_get_clk(bus, &priv->bus_clk_rate);
@@ -1022,7 +1042,10 @@ static const struct udevice_id dw_spi_ids[] = {
 	 */
 	{ .compatible = "altr,socfpga-spi" },
 	{ .compatible = "altr,socfpga-arria10-spi" },
-	{ .compatible = "canaan,kendryte-k210-spi" },
+	{
+		.compatible = "canaan,kendryte-k210-spi",
+		.data = DW_SPI_CAP_XIP,
+	},
 	{
 		.compatible = "canaan,kendryte-k210-ssi",
 		.data = DW_SPI_CAP_DWC_SSI,
-- 
2.29.2

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

* [RFC PATCH 10/13] spi: dw: Use a mux to access registers
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
                   ` (8 preceding siblings ...)
  2021-02-05  4:39 ` [RFC PATCH 09/13] spi: dw: Add XIP and XIP_CONCURRENT caps Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-05  4:39 ` [RFC PATCH 11/13] spi: dw: Add support for DIRMAP Sean Anderson
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

To enter XIP mode, the xip_en signal must be asserted. The exact method of
setting xip_en is integration-specific, but on the K210 (and Baikal-T1) it
is set by a bit in a system configuration register. To handle this, use a
mux to select the state of xip_en before every access to control registers.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 drivers/spi/designware_spi.c | 133 ++++++++++++++++++++++++++++++++++-
 1 file changed, 130 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index c41c5b4982..6f74a471e3 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -19,6 +19,7 @@
 #include <fdtdec.h>
 #include <log.h>
 #include <malloc.h>
+#include <mux.h>
 #include <reset.h>
 #include <spi.h>
 #include <spi-mem.h>
@@ -209,6 +210,7 @@ struct dw_spi_priv {
 	struct clk clk;
 	struct reset_ctl_bulk resets;
 	struct gpio_desc cs_gpio;	/* External chip-select gpio */
+	struct mux_control *mux;	/* XIP mode mux */
 
 	void __iomem *regs;
 	fdt_size_t regs_size;
@@ -225,6 +227,7 @@ struct dw_spi_priv {
 	unsigned int freq;		/* Default frequency */
 	unsigned int mode;
 
+	u32 mux_xip_state;		/* Mux state to enable XIP mode */
 	u32 fifo_len;			/* depth of the FIFO buffer */
 
 	int bits_per_word;
@@ -346,11 +349,79 @@ static int dw_spi_of_to_plat(struct udevice *bus)
 	return request_gpio_cs(bus);
 }
 
-/* Restart the controller, disable all interrupts, clean rx fifo */
-static void spi_hw_init(struct udevice *bus, struct dw_spi_priv *priv)
+static int dw_spi_mux(struct udevice *dev, bool xip)
 {
+	struct dw_spi_priv *priv = dev_get_priv(dev);
+
+	if (!priv->mux)
+		return 0;
+
+	if (xip && priv->mux_xip_state)
+		return mux_control_select(priv->mux, priv->mux_xip_state);
+	else
+		return mux_control_select(priv->mux, 0);
+}
+
+/*
+ * dw_spi_mux_regs() - Select the control registers using the XIP mux
+ * @dev: The device to mux
+ *
+ * This selects the control registers using the XIP mux, driving the xip_en
+ * signal low. This function must be called before any accesses to control
+ * registers.
+ *
+ * Return: 0 on success or negative error value
+ */
+static inline int dw_spi_mux_regs(struct udevice *dev)
+{
+	return dw_spi_mux(dev, false);
+}
+
+/*
+ * dw_spi_mux_xip() - Select the control registers using the XIP mux
+ * @dev: The device to mux
+ *
+ * This selects XIP mode using the XIP mux, driving the xip_en signal high. This
+ * function must be called before any XIP accesses.
+ *
+ * Return: 0 on success or negative error value
+ */
+static inline int dw_spi_mux_xip(struct udevice *dev)
+{
+	return dw_spi_mux(dev, true);
+}
+
+/*
+ * dw_spi_mux_deselect()
+ * @dev: The device to mux
+ *
+ * This deselects the XIP mux, returning it to its default state. This must be
+ * called after control register or XIP accesses are finished, before other
+ * calls to @dw_spi_mux_regs or @dw_spi_mux_xip.
+ */
+static void dw_spi_mux_deselect(struct udevice *dev)
+{
+	int err;
+	struct dw_spi_priv *priv = dev_get_priv(dev);
+
+	if (!priv->mux)
+		return;
+
+	err = mux_control_deselect(priv->mux);
+	if (err)
+		dev_warn(dev, "could not deselect mux (err %d)\n", err);
+}
+
+/* Restart the controller, disable all interrupts, clean rx fifo */
+static int spi_hw_init(struct udevice *bus, struct dw_spi_priv *priv)
+{
+	int ret;
 	u32 cr0;
 
+	ret = dw_spi_mux_regs(bus);
+	if (ret)
+		return ret;
+
 	dw_write(priv, DW_SPI_SSIENR, 0);
 	dw_write(priv, DW_SPI_IMR, 0);
 
@@ -415,6 +486,9 @@ static void spi_hw_init(struct udevice *bus, struct dw_spi_priv *priv)
 
 	/* Set receive fifo interrupt level register for clock stretching */
 	dw_write(priv, DW_SPI_RXFTLR, priv->fifo_len - 1);
+
+	dw_spi_mux_deselect(bus);
+	return 0;
 }
 
 /*
@@ -480,6 +554,33 @@ static int dw_spi_reset(struct udevice *bus)
 	return 0;
 }
 
+int dw_spi_get_mux(struct udevice *bus)
+{
+	int ret;
+	struct dw_spi_priv *priv = dev_get_priv(bus);
+
+	ret = mux_get_by_index(bus, 0, &priv->mux);
+	if (ret) {
+		/*
+		 * Return 0 if error due to !CONFIG_MUX or mux
+		 * DT property is not present.
+		 */
+		if (ret == -ENOENT || ret == -ENOTSUPP)
+			return 0;
+
+		dev_warn(bus, "Couldn't get xip mux (error %d)\n", ret);
+		return ret;
+	}
+
+	ret = dev_read_u32(bus, "mux-xip-state", &priv->mux_xip_state);
+	if (ret || priv->mux_xip_state > 1) {
+		dev_warn(bus, "Invalid/missing mux-xip-state property\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int dw_spi_probe(struct udevice *bus)
 {
 	struct dw_spi_plat *plat = dev_get_plat(bus);
@@ -499,6 +600,10 @@ static int dw_spi_probe(struct udevice *bus)
 	if (ret)
 		return ret;
 
+	ret = dw_spi_get_mux(bus);
+	if (ret)
+		return ret;
+
 	/* Currently only bits_per_word == 8 supported */
 	priv->bits_per_word = 8;
 
@@ -506,7 +611,12 @@ static int dw_spi_probe(struct udevice *bus)
 
 	/* Basic HW init */
 	priv->caps = dev_get_driver_data(bus);
-	spi_hw_init(bus, priv);
+	ret = spi_hw_init(bus, priv);
+	if (ret)
+		return ret;
+
+	if (!priv->mux)
+		priv->caps &= DW_SPI_CAP_XIP;
 
 	version = dw_read(priv, DW_SPI_VERSION);
 	dev_dbg(bus,
@@ -713,6 +823,10 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
 		return -1;
 	}
 
+	ret = dw_spi_mux_regs(bus);
+	if (ret)
+		return ret;
+
 	frames = bitlen / priv->bits_per_word;
 
 	/* Start the transaction if necessary. */
@@ -779,6 +893,8 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
 	if (flags & SPI_XFER_END)
 		external_cs_manage(dev, true);
 
+	dw_spi_mux_deselect(bus);
+
 	return ret;
 }
 
@@ -831,6 +947,10 @@ static int dw_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
 	else
 		priv->tmode = CTRLR0_TMOD_TO;
 
+	ret = dw_spi_mux_regs(bus);
+	if (ret)
+		return ret;
+
 	cr0 = dw_spi_update_cr0(priv);
 	spi_cr0 = dw_spi_update_spi_cr0(op);
 	dev_dbg(bus, "cr0=%08x spi_cr0=%08x buf=%p len=%u [bytes]\n", cr0,
@@ -891,6 +1011,7 @@ static int dw_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
 	}
 	dw_write(priv, DW_SPI_SER, 0);
 	external_cs_manage(slave->dev, true);
+	dw_spi_mux_deselect(bus);
 
 	dev_dbg(bus, "%u bytes xfered\n", op->data.nbytes);
 	return ret;
@@ -943,10 +1064,15 @@ static const struct spi_controller_mem_ops dw_spi_mem_ops = {
 
 static int dw_spi_set_speed(struct udevice *bus, uint speed)
 {
+	int ret;
 	struct dw_spi_plat *plat = dev_get_plat(bus);
 	struct dw_spi_priv *priv = dev_get_priv(bus);
 	u16 clk_div;
 
+	ret = dw_spi_mux_regs(bus);
+	if (ret)
+		return ret;
+
 	if (speed > plat->frequency)
 		speed = plat->frequency;
 
@@ -960,6 +1086,7 @@ static int dw_spi_set_speed(struct udevice *bus, uint speed)
 
 	/* Enable controller after writing control registers */
 	dw_write(priv, DW_SPI_SSIENR, 1);
+	mux_control_deselect(priv->mux);
 
 	priv->freq = speed;
 	dev_dbg(bus, "speed=%d clk_div=%d\n", priv->freq, clk_div);
-- 
2.29.2

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

* [RFC PATCH 11/13] spi: dw: Add support for DIRMAP
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
                   ` (9 preceding siblings ...)
  2021-02-05  4:39 ` [RFC PATCH 10/13] spi: dw: Use a mux to access registers Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-05  4:39 ` [RFC PATCH 12/13] riscv: k210: Increase SPI3 bus clock to CPU speed Sean Anderson
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

This adds support for XIP mode. It is not actually any faster than QPI
(yet), but it serves as a good starting point for using XIP mode for other
purposes (such as actual eXecuting In Place).

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 drivers/spi/designware_spi.c | 131 +++++++++++++++++++++++++++++++++++
 1 file changed, 131 insertions(+)

diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index 6f74a471e3..cb7a28c3bf 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -1056,10 +1056,141 @@ static int dw_spi_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(SPI_DIRMAP)
+static int dw_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
+{
+	struct dw_spi_priv *priv = dev_get_priv(desc->slave->dev->parent);
+
+	/*
+	 * Currently only DWC XIP is supported. DW APB SSI XIP exists, but
+	 * cannot send an instruction before the address, so it is left for when
+	 * U-Boot supports 0-X-X instructions. In addition, we only support
+	 * concurrent XIP (since I have no non-condcurrent XIP hardware to test
+	 * with)
+	 */
+	if (!(priv->caps & (DW_SPI_CAP_XIP)) ||
+	    !(priv->caps & (DW_SPI_CAP_DWC_SSI)) ||
+	    !(priv->caps & (DW_SPI_CAP_XIP_CONCURRENT)))
+		return -ENOTSUPP;
+
+	if (!spi_mem_supports_op(desc->slave, &desc->info.op_tmpl))
+		return -ENOTSUPP;
+
+	/*
+	 * Make sure the requested region doesn't go out of the physically
+	 * mapped flash memory bounds and the operation is read-only.
+	 */
+	if (desc->info.offset + desc->info.length > priv->regs_size ||
+	    desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
+		return -ENOTSUPP;
+
+	/* XIP only supports enhanced SPI modes */
+	if (desc->info.op_tmpl.data.buswidth == 1)
+		return -ENOTSUPP;
+
+	return 0;
+}
+
+static u32 dw_spi_update_xip_cr(const struct spi_mem_op *op, uint frf)
+{
+	uint trans_type, wait_cycles;
+
+	/* This assumes support_op has filtered invalid types */
+	if (op->addr.buswidth == 1)
+		trans_type = TRANS_TYPE_1_1_X;
+	else if (op->cmd.buswidth == 1)
+		trans_type = TRANS_TYPE_1_X_X;
+	else
+		trans_type = TRANS_TYPE_X_X_X;
+
+	if (op->dummy.buswidth)
+		wait_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth;
+	else
+		wait_cycles = 0;
+
+	return FIELD_PREP(XIP_CTRL_FRF, frf)
+	       | FIELD_PREP(XIP_CTRL_TRANS_TYPE_MASK, trans_type)
+	       | FIELD_PREP(XIP_CTRL_ADDR_L_MASK, op->addr.nbytes * 2)
+	       | FIELD_PREP(XIP_CTRL_INST_L_MASK, INST_L_8)
+	       | FIELD_PREP(XIP_CTRL_WAIT_CYCLES_MASK, wait_cycles)
+	       //| XIP_CTRL_DFS_HC
+	       | XIP_CTRL_INST_EN
+	       | XIP_CTRL_CONT_XFER_EN
+	       | XIP_CTRL_PREFETCH_EN;
+}
+
+static ssize_t dw_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, u64 offs,
+				  size_t len, void *buf)
+{
+	int ret;
+	size_t count = len;
+	struct spi_slave *slave = desc->slave;
+	struct udevice *bus = slave->dev->parent;
+	struct dw_spi_priv *priv = dev_get_priv(bus);
+	struct spi_mem_op *op = &desc->info.op_tmpl;
+	u8 *from, *to;
+
+	switch (op->data.buswidth) {
+	case 2:
+		priv->spi_frf = CTRLR0_SPI_FRF_DUAL;
+		break;
+	case 4:
+		priv->spi_frf = CTRLR0_SPI_FRF_QUAD;
+		break;
+	case 8:
+		priv->spi_frf = CTRLR0_SPI_FRF_OCTAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = dw_spi_mux_ctrl(bus);
+	if (ret)
+		return ret;
+
+	dw_write(priv, DW_SPI_SSIENR, 0);
+	//dw_write(priv, DW_SPI_CTRLR0, dw_spi_update_cr0(priv));
+	dw_write(priv, DW_SPI_XIP_CTRL, dw_spi_update_xip_cr(op, priv->spi_frf));
+	dw_write(priv, DW_SPI_XIP_INCR_INST, op->cmd.opcode);
+	/*
+	 * FIXME: U-Boot doesn't currently support wrap instructions, but we
+	 * can't control what the AHB master does. Just write 0 to get something
+	 * obviously bogus.
+	 */
+	dw_write(priv, DW_SPI_XIP_WRAP_INST, 0);
+	dw_write(priv, DW_SPI_XIP_SER, 1 << spi_chip_select(slave->dev));
+	dw_write(priv, DW_SPI_SSIENR, 1);
+
+	dw_spi_mux_deselect(bus);
+
+	external_cs_manage(slave->dev, true);
+
+	ret = dw_spi_mux_xip(bus);
+	if (ret)
+		return ret;
+
+	//memcpy(buf, priv->regs + offs, len);
+	from = priv->regs + offs;
+	to = buf;
+	while (count--)
+		*to++ = *from++;
+
+	dw_spi_mux_deselect(bus);
+
+	external_cs_manage(slave->dev, false);
+
+	return len;
+}
+#endif /* CONFIG_SPI_DIRMAP */
+
 static const struct spi_controller_mem_ops dw_spi_mem_ops = {
 	.exec_op = dw_spi_exec_op,
 	.supports_op = dw_spi_supports_op,
 	.adjust_op_size = dw_spi_adjust_op_size,
+#if CONFIG_IS_ENABLED(SPI_DIRMAP)
+	.dirmap_create = dw_spi_dirmap_create,
+	.dirmap_read = dw_spi_dirmap_read,
+#endif
 };
 
 static int dw_spi_set_speed(struct udevice *bus, uint speed)
-- 
2.29.2

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

* [RFC PATCH 12/13] riscv: k210: Increase SPI3 bus clock to CPU speed
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
                   ` (10 preceding siblings ...)
  2021-02-05  4:39 ` [RFC PATCH 11/13] spi: dw: Add support for DIRMAP Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-05  4:39 ` [RFC PATCH 13/13] riscv: k210: Add bindings for SPI XIP Sean Anderson
  2021-02-05  4:43 ` [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
  13 siblings, 0 replies; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

By default the SPI3 bus clock is ~100MHz, 1/4th of the CPU clock. This
causes decreased performance when accessing this peripheral.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 arch/riscv/dts/k210.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/riscv/dts/k210.dtsi b/arch/riscv/dts/k210.dtsi
index 81b04018c6..dac7c62289 100644
--- a/arch/riscv/dts/k210.dtsi
+++ b/arch/riscv/dts/k210.dtsi
@@ -600,6 +600,8 @@
 				interrupts = <4>;
 				clocks = <&sysclk K210_CLK_SPI3>;
 				clock-names = "ssi_clk";
+				assigned-clocks = <&sysclk K210_CLK_SPI3>;
+				assigned-clock-rates = <390000000>;
 				resets = <&sysrst K210_RST_SPI3>;
 				/* Could possibly go up to 200 MHz */
 				spi-max-frequency = <100000000>;
-- 
2.29.2

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

* [RFC PATCH 13/13] riscv: k210: Add bindings for SPI XIP
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
                   ` (11 preceding siblings ...)
  2021-02-05  4:39 ` [RFC PATCH 12/13] riscv: k210: Increase SPI3 bus clock to CPU speed Sean Anderson
@ 2021-02-05  4:39 ` Sean Anderson
  2021-02-05  4:43 ` [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
  13 siblings, 0 replies; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:39 UTC (permalink / raw)
  To: u-boot

On the K210, a register in sysctl controls whether accessing an SPI device
has the xip_en signal set. Add the appropriate bindings.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 arch/riscv/dts/k210.dtsi | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git a/arch/riscv/dts/k210.dtsi b/arch/riscv/dts/k210.dtsi
index dac7c62289..aa1c53f09f 100644
--- a/arch/riscv/dts/k210.dtsi
+++ b/arch/riscv/dts/k210.dtsi
@@ -292,6 +292,8 @@
 				interrupts = <2>;
 				clocks = <&sysclk K210_CLK_SPI2>;
 				resets = <&sysrst K210_RST_SPI2>;
+				mux-controls = <&xip_mux 2>;
+				mux-xip-state = <1>;
 				spi-max-frequency = <25000000>;
 				status = "disabled";
 			};
@@ -530,6 +532,15 @@
 					mask = <1>;
 					value = <1>;
 				};
+
+				xip_mux: mux-controller {
+					compatible = "mmio-mux";
+					#mux-control-cells = <1>;
+					mux-reg-masks = <K210_SYSCTL_PERI (1 << 12)>,
+							<K210_SYSCTL_PERI (1 << 13)>,
+							<K210_SYSCTL_PERI (1 << 14)>,
+							<K210_SYSCTL_PERI (1 << 15)>;
+				};
 			};
 
 			aes0: aes at 50450000 {
@@ -563,11 +574,13 @@
 				compatible = "canaan,kendryte-k210-spi",
 					     "snps,dw-apb-ssi-4.01",
 					     "snps,dw-apb-ssi";
-				reg = <0x52000000 0x100>;
+				reg = <0x52000000 0x1000000>;
 				interrupts = <1>;
 				clocks = <&sysclk K210_CLK_SPI0>;
 				clock-names = "ssi_clk";
 				resets = <&sysrst K210_RST_SPI0>;
+				mux-controls = <&xip_mux 0>;
+				mux-xip-state = <1>;
 				spi-max-frequency = <25000000>;
 				num-cs = <4>;
 				reg-io-width = <4>;
@@ -580,11 +593,13 @@
 				compatible = "canaan,kendryte-k210-spi",
 					     "snps,dw-apb-ssi-4.01",
 					     "snps,dw-apb-ssi";
-				reg = <0x53000000 0x100>;
+				reg = <0x53000000 0x1000000>;
 				interrupts = <2>;
 				clocks = <&sysclk K210_CLK_SPI1>;
 				clock-names = "ssi_clk";
 				resets = <&sysrst K210_RST_SPI1>;
+				mux-controls = <&xip_mux 1>;
+				mux-xip-state = <1>;
 				spi-max-frequency = <25000000>;
 				num-cs = <4>;
 				reg-io-width = <4>;
@@ -596,13 +611,15 @@
 				#size-cells = <0>;
 				compatible = "canaan,kendryte-k210-ssi",
 					     "snps,dwc-ssi-1.01a";
-				reg = <0x54000000 0x200>;
+				reg = <0x54000000 0x1000000>;
 				interrupts = <4>;
 				clocks = <&sysclk K210_CLK_SPI3>;
 				clock-names = "ssi_clk";
 				assigned-clocks = <&sysclk K210_CLK_SPI3>;
 				assigned-clock-rates = <390000000>;
 				resets = <&sysrst K210_RST_SPI3>;
+				mux-controls = <&xip_mux 3>;
+				mux-xip-state = <1>;
 				/* Could possibly go up to 200 MHz */
 				spi-max-frequency = <100000000>;
 				num-cs = <4>;
-- 
2.29.2

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

* [RFC PATCH 00/13] spi: dw: Add support for XIP mode
  2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
                   ` (12 preceding siblings ...)
  2021-02-05  4:39 ` [RFC PATCH 13/13] riscv: k210: Add bindings for SPI XIP Sean Anderson
@ 2021-02-05  4:43 ` Sean Anderson
  13 siblings, 0 replies; 21+ messages in thread
From: Sean Anderson @ 2021-02-05  4:43 UTC (permalink / raw)
  To: u-boot

On 2/4/21 11:39 PM, Sean Anderson wrote:
> This adds support for memory-mapped ("DIRMAP") reads (called XIP by the
> datasheet). In theory, these have better performance than regular reads. In
> practice, the CPU is already fast enough to max out the performance of this
> peripheral.
> 
> The real end-goal is to be able to boot a XIP kernel. This SoC has just barely
> enough SRAM to boot Linux, and using a XIP kernel would decrease the memory
> pressure to something tolerable. It would also probably absolutely *tank* the
> performance, since not only is SPI flash ~8x slower than regular memory, there
> is no cache to mitigate that latency. So it remains to be seen whether this is
> worth it at all.
> 
> This series is RFC because it currently reads all 1s on one board I have. On the
> other board I have, it reads and writes fine. I will try and investigate this,
> but I expect any changes to occur in "spi: dw: Add support for DIRMAP".

Forgot to mention, but this series depends on [1]. A git branch with
this complete series is available at [2].

[1] https://patchwork.ozlabs.org/project/uboot/list/?series=228123
[2] https://github.com/Forty-Bot/u-boot/tree/maix_xip

> 
> Sean Anderson (13):
>    linux err: Synchronize with Linux 5.10
>    spi-mem: Add dirmap API from Linux
>    mtd: spi-nor: use spi-mem dirmap API
>    core: ofnode: Fix inconsistent returns of *_read_u32_array
>    mux: Inline mux functions when CONFIG_MUX is disabled
>    mux: Define a stub for mux_get_by_index if CONFIG_MUX is disabled
>    mux: mmio: Only complain about idle-states if it is malformed
>    spi: dw: Define XIP registers
>    spi: dw: Add XIP and XIP_CONCURRENT caps
>    spi: dw: Use a mux to access registers
>    spi: dw: Add support for DIRMAP
>    riscv: k210: Increase SPI3 bus clock to CPU speed
>    riscv: k210: Add bindings for SPI XIP
> 
>   arch/riscv/dts/k210.dtsi       |  25 ++-
>   drivers/core/ofnode.c          |  15 +-
>   drivers/mtd/spi/sf_probe.c     |  79 ++++++++
>   drivers/mtd/spi/spi-nor-core.c |  45 +++--
>   drivers/mux/mmio.c             |   5 +-
>   drivers/spi/Kconfig            |  10 +
>   drivers/spi/designware_spi.c   | 337 +++++++++++++++++++++++++++++++--
>   drivers/spi/spi-mem.c          | 270 ++++++++++++++++++++++++++
>   include/linux/err.h            |  21 +-
>   include/linux/mtd/spi-nor.h    |   6 +
>   include/mux.h                  |  25 ++-
>   include/spi-mem.h              |  99 ++++++++++
>   12 files changed, 889 insertions(+), 48 deletions(-)
> 

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

* [RFC PATCH 05/13] mux: Inline mux functions when CONFIG_MUX is disabled
  2021-02-05  4:39 ` [RFC PATCH 05/13] mux: Inline mux functions when CONFIG_MUX is disabled Sean Anderson
@ 2021-02-05  7:32   ` Pratyush Yadav
  0 siblings, 0 replies; 21+ messages in thread
From: Pratyush Yadav @ 2021-02-05  7:32 UTC (permalink / raw)
  To: u-boot

On 04/02/21 11:39PM, Sean Anderson wrote:
> This prevents multiple-definition errors.
> 
> Fixes: 0ad40b2463 ("drivers: Add a new framework for multiplexer devices")
> 
> Signed-off-by: Sean Anderson <seanga2@gmail.com>

Reviewed-by: Pratyush Yadav <p.yadav@ti.com>

-- 
Regards,
Pratyush Yadav
Texas Instruments Inc.

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

* [RFC PATCH 06/13] mux: Define a stub for mux_get_by_index if CONFIG_MUX is disabled
  2021-02-05  4:39 ` [RFC PATCH 06/13] mux: Define a stub for mux_get_by_index if " Sean Anderson
@ 2021-02-05 10:53   ` Pratyush Yadav
  0 siblings, 0 replies; 21+ messages in thread
From: Pratyush Yadav @ 2021-02-05 10:53 UTC (permalink / raw)
  To: u-boot

On 04/02/21 11:39PM, Sean Anderson wrote:
> This allows code using mux_get_by_index to be agnostic about CONFIG_MUX.
> 
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
> 
>  include/mux.h | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/include/mux.h b/include/mux.h
> index 91a89bbb9a..882a00068b 100644
> --- a/include/mux.h
> +++ b/include/mux.h
> @@ -135,6 +135,12 @@ static inline int mux_control_deselect(struct mux_control *mux)
>  	return -ENOSYS;
>  }
>  
> +static inline int mux_get_by_index(struct udevice *dev, int index,
> +				   struct mux_control **mux)
> +{
> +	return -ENOSYS;
> +}
> +
>  static inline struct mux_control *mux_control_get(struct udevice *dev,
>  						  const char *mux_name)
>  {

Hmm... mux_control_get() on the other side of this ifdef has a 
completely different signature. Can you please add that fix in your 
series as well? For this patch,

Reviewed-by: Pratyush Yadav <p.yadav@ti.com>

-- 
Regards,
Pratyush Yadav
Texas Instruments Inc.

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

* [RFC PATCH 07/13] mux: mmio: Only complain about idle-states if it is malformed
  2021-02-05  4:39 ` [RFC PATCH 07/13] mux: mmio: Only complain about idle-states if it is malformed Sean Anderson
@ 2021-02-05 11:06   ` Pratyush Yadav
  2021-02-05 13:28     ` Sean Anderson
  0 siblings, 1 reply; 21+ messages in thread
From: Pratyush Yadav @ 2021-02-05 11:06 UTC (permalink / raw)
  To: u-boot

On 04/02/21 11:39PM, Sean Anderson wrote:
> idle-states is optional, so don't complain if it doesn't exist.

This commit doesn't just silence the complaint. It also changes the 
behavior of the function if the error code is ENODATA or EOVERFLOW. Make 
sure the commit message reflects that.

> 
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
> 
>  drivers/mux/mmio.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c
> index 00e0282dcc..82b1cc6aab 100644
> --- a/drivers/mux/mmio.c
> +++ b/drivers/mux/mmio.c
> @@ -87,8 +87,11 @@ static int mmio_mux_probe(struct udevice *dev)
>  
>  	ret = dev_read_u32_array(dev, "idle-states", idle_states, num_fields);
>  	if (ret < 0) {
> -		log_err("idle-states");
>  		devm_kfree(dev, idle_states);
> +		/* dev_read_u32_array returns -EINVAL on missing property */
> +		if (ret != -EINVAL)
> +			return log_msg_ret("idle-states", -EINVAL);

Return ret here. I don't see any reason to return -EINVAL when the error 
is _not_ -EINVAL.

> +
>  		idle_states = NULL;
>  	}
>  
> -- 
> 2.29.2
> 

-- 
Regards,
Pratyush Yadav
Texas Instruments Inc.

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

* [RFC PATCH 07/13] mux: mmio: Only complain about idle-states if it is malformed
  2021-02-05 11:06   ` Pratyush Yadav
@ 2021-02-05 13:28     ` Sean Anderson
  0 siblings, 0 replies; 21+ messages in thread
From: Sean Anderson @ 2021-02-05 13:28 UTC (permalink / raw)
  To: u-boot

On 2/5/21 6:06 AM, Pratyush Yadav wrote:
> On 04/02/21 11:39PM, Sean Anderson wrote:
>> idle-states is optional, so don't complain if it doesn't exist.
> 
> This commit doesn't just silence the complaint. It also changes the
> behavior of the function if the error code is ENODATA or EOVERFLOW. Make
> sure the commit message reflects that.

Sure.

> 
>>
>> Signed-off-by: Sean Anderson <seanga2@gmail.com>
>> ---
>>
>>   drivers/mux/mmio.c | 5 ++++-
>>   1 file changed, 4 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/mux/mmio.c b/drivers/mux/mmio.c
>> index 00e0282dcc..82b1cc6aab 100644
>> --- a/drivers/mux/mmio.c
>> +++ b/drivers/mux/mmio.c
>> @@ -87,8 +87,11 @@ static int mmio_mux_probe(struct udevice *dev)
>>   
>>   	ret = dev_read_u32_array(dev, "idle-states", idle_states, num_fields);
>>   	if (ret < 0) {
>> -		log_err("idle-states");
>>   		devm_kfree(dev, idle_states);
>> +		/* dev_read_u32_array returns -EINVAL on missing property */
>> +		if (ret != -EINVAL)
>> +			return log_msg_ret("idle-states", -EINVAL);
> 
> Return ret here. I don't see any reason to return -EINVAL when the error
> is _not_ -EINVAL.

EINVAL is the traditional return value for when a binding is malformed.
Though I don't mind returning ENODATA or EOVERFLOW here. Will be
updated.

--Sean

> 
>> +
>>   		idle_states = NULL;
>>   	}
>>   
>> -- 
>> 2.29.2
>>
> 

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

* [RFC PATCH 01/13] linux err: Synchronize with Linux 5.10
  2021-02-05  4:39 ` [RFC PATCH 01/13] linux err: Synchronize with Linux 5.10 Sean Anderson
@ 2021-02-07 14:37   ` Simon Glass
  0 siblings, 0 replies; 21+ messages in thread
From: Simon Glass @ 2021-02-07 14:37 UTC (permalink / raw)
  To: u-boot

On Thu, 4 Feb 2021 at 21:39, Sean Anderson <seanga2@gmail.com> wrote:
>
> This synchronizes linux/err.h with Linux 5.10. Notably, this adds
> PTR_ERR_OR_ZERO.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
>
>  include/linux/err.h | 21 +++++++++++++++------
>  1 file changed, 15 insertions(+), 6 deletions(-)
>

Reviewed-by: Simon Glass <sjg@chromium.org>

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

* [RFC PATCH 04/13] core: ofnode: Fix inconsistent returns of *_read_u32_array
  2021-02-05  4:39 ` [RFC PATCH 04/13] core: ofnode: Fix inconsistent returns of *_read_u32_array Sean Anderson
@ 2021-02-07 14:37   ` Simon Glass
  0 siblings, 0 replies; 21+ messages in thread
From: Simon Glass @ 2021-02-07 14:37 UTC (permalink / raw)
  To: u-boot

On Thu, 4 Feb 2021 at 21:39, Sean Anderson <seanga2@gmail.com> wrote:
>
> The documentation for dev_read_u32_array says the return value is an errno,
> but fdtdec_get_int_array returns FDT_ERRs. Convert the return values so
> callers can handle errors properly.
>
> Signed-off-by: Sean Anderson <seanga2@gmail.com>
> ---
>
>  drivers/core/ofnode.c | 15 ++++++++++++---
>  1 file changed, 12 insertions(+), 3 deletions(-)

Reviewed-by: Simon Glass <sjg@chromium.org>

This could really use a driver model test so that both options are
automatically checked.

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

end of thread, other threads:[~2021-02-07 14:37 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-05  4:39 [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson
2021-02-05  4:39 ` [RFC PATCH 01/13] linux err: Synchronize with Linux 5.10 Sean Anderson
2021-02-07 14:37   ` Simon Glass
2021-02-05  4:39 ` [RFC PATCH 02/13] spi-mem: Add dirmap API from Linux Sean Anderson
2021-02-05  4:39 ` [RFC PATCH 03/13] mtd: spi-nor: use spi-mem dirmap API Sean Anderson
2021-02-05  4:39 ` [RFC PATCH 04/13] core: ofnode: Fix inconsistent returns of *_read_u32_array Sean Anderson
2021-02-07 14:37   ` Simon Glass
2021-02-05  4:39 ` [RFC PATCH 05/13] mux: Inline mux functions when CONFIG_MUX is disabled Sean Anderson
2021-02-05  7:32   ` Pratyush Yadav
2021-02-05  4:39 ` [RFC PATCH 06/13] mux: Define a stub for mux_get_by_index if " Sean Anderson
2021-02-05 10:53   ` Pratyush Yadav
2021-02-05  4:39 ` [RFC PATCH 07/13] mux: mmio: Only complain about idle-states if it is malformed Sean Anderson
2021-02-05 11:06   ` Pratyush Yadav
2021-02-05 13:28     ` Sean Anderson
2021-02-05  4:39 ` [RFC PATCH 08/13] spi: dw: Define XIP registers Sean Anderson
2021-02-05  4:39 ` [RFC PATCH 09/13] spi: dw: Add XIP and XIP_CONCURRENT caps Sean Anderson
2021-02-05  4:39 ` [RFC PATCH 10/13] spi: dw: Use a mux to access registers Sean Anderson
2021-02-05  4:39 ` [RFC PATCH 11/13] spi: dw: Add support for DIRMAP Sean Anderson
2021-02-05  4:39 ` [RFC PATCH 12/13] riscv: k210: Increase SPI3 bus clock to CPU speed Sean Anderson
2021-02-05  4:39 ` [RFC PATCH 13/13] riscv: k210: Add bindings for SPI XIP Sean Anderson
2021-02-05  4:43 ` [RFC PATCH 00/13] spi: dw: Add support for XIP mode Sean Anderson

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.