All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/6] spi: Extend the framework to generically support memory devices
@ 2018-02-05 23:21 ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar,
	Boris Brezillon

Hello all,

I know a lot of you have already tried to address some of the flaws in
the SPI NOR framework, and I'm coming with yet another proposal to solve
some of the problems I faced recently :-/. The approach I'm proposing
here has been motivated by the recent work done on SPI NANDs.

Peter has proposed a SPI NAND controller interface (based on my
suggestions) where the controller had to implement hooks that the SPI
NAND framework could use to execute SPI NAND operations. I thought the
approach was acceptable as a first step before we move to a solution
where we share even more logic between SPI NOR and SPI NAND frameworks.
But I recently changed my mind after working with Frieder to support
his SPI NAND (Frieder has a SPI NAND connected to the FSL QSPI
controller).

And here come the problems: we definitely want one FSL QSPI driver
that works for both NANDs and NORs. In the current state, that means
forcing the FSL QSPI driver to implement both the SPI NAND and SPI NOR
interfaces. When you look at the internal controller logic, if we want
to support this without duplicating too much code, we have to add
an intermediate representation that abstracts away the NOR/NAND
specificities and pass only basic information to the controller:

* the opcode
* the number of address cycles
* the number of dummy cycles
* the number of data bytes to receive/transmit

And the same problem is likely to hit most of the controller drivers
placed in drivers/mtd/spi-nor.

Another problem I see with the current approach is the duplication
of DT parsing code in almost all drivers, even though most of them
follow the generic SPI controller/device bindings.

And finally, there's the confusion brought by the 2 entry points
exposed by the SPI NOR layer (and that we had planned to mimic in the
SPI NAND layer): you can register a SPI NOR device directly from the
dedicated SPI memory controller, or it can be registered through the
SPI layer if the SPI controller is a generic SPI controller. While
the generic SPI controller path works fine, the dedicated SPI NOR
controller path brings its own set of issues:

* the SPI bus is not represented in sysfs
* because there's no bus, there's no uevent, which means you'll have to
  select both the SPI NAND and SPI NOR logic as soon as one driver
  supports both interfaces if you don't want to run into loading
  dependency issues

Not to mention that, in order to work properly, the SPI flash ids may
have to be exported so that all QSPI controller drivers can use the
same database.

For all these reasons, I decided to try a new approach: move the
generic "SPI memory" logic in the SPI framework (what I call the SPI
memory interface here is actually not limited to SPI memories, and
I'm pretty sure other QSPI devices use the same kind of protocol).

So, basically, all kind of operations that can be done on a SPI
memory are encoded with a  CMD[+ADDR][+DUMMY][+DATA_{IN,OUT}] sequence,
and that's pretty much what I'm adding here: a way to ask the SPI
framework to execute a SPI memory operation. If the SPI controller
this memory is connected to is a generic SPI controller, then those
operations will be converted to generic SPI messages, but, if the
controller has an high-level interface to send those operations, then
it can expose it and the framework will use it.

One last comment: this new interface makes the spi_flash_read() API
redundant, because it's able to handle both read/write path and allows
the same kind of optimizations you could achieve with the
spi_flash_read hooks.

To sum-up: the idea behind this series is to converge to a
common/generic interface that could be used by any kind of SPI memory
driver and still allow per-controller optimizations.

Right now, the design is quite simple, and drivers can easily implement
the various mem_ops to optimize things. I'm currently working on the
conversion of the FSL driver, but I'm pretty sure most of the QSPI
drivers lying in drivers/mtd/spi-nor can easily be converted to this
new interface.

Once, we'll have that done, the SPI NOR layer will declare itself as
a regular spi_mem_driver and we'll be done with all the funky things
we have there. The SPI NAND patches have already been converted to this
approach.

If you want to see the big picture, my development branch is here [1]
and contains this series + the SPI NAND patches + a hack-ish FSL
QSPI ported to this driver which will hopefully support both SPI NANDs
and SPI NORs (only tested with a SPI NAND so far).

I know it's a big introduction, and I probably have lost most of my
readers before this point :-). But I'd really like to have feedback on
this series, because I'd hate to have to add yet another layer of
insane hacks in existing QSPI drivers to support both NORs and NANDs.

Mark, Cyrille, Marek, and all people that have been involved in SPI-NOR
development lately, please have a quick look at this RFC, and let me
know what you think.

Thanks,

Boris

[1]https://github.com/bbrezillon/linux/tree/spi-mem

Boris Brezillon (6):
  spi: Extend the core to ease integration of SPI memory controllers
  spi: bcm-qspi: Implement the spi_mem interface
  spi: bcm53xx: Implement the spi_mem interface
  spi: ti-qspi: Implement the spi_mem interface
  mtd: spi-nor: Use the spi_mem_xx() API
  spi: Get rid of the spi_flash_read() API

 drivers/mtd/devices/m25p80.c | 240 ++++++++--------------
 drivers/spi/spi-bcm-qspi.c   | 173 ++++++++--------
 drivers/spi/spi-bcm53xx.c    |  38 +++-
 drivers/spi/spi-ti-qspi.c    |  86 +++++---
 drivers/spi/spi.c            | 480 +++++++++++++++++++++++++++++++++++++------
 include/linux/spi/spi.h      | 263 ++++++++++++++++++++----
 6 files changed, 898 insertions(+), 382 deletions(-)

-- 
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [RFC PATCH 0/6] spi: Extend the framework to generically support memory devices
@ 2018-02-05 23:21 ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar,
	Boris Brezillon

Hello all,

I know a lot of you have already tried to address some of the flaws in
the SPI NOR framework, and I'm coming with yet another proposal to solve
some of the problems I faced recently :-/. The approach I'm proposing
here has been motivated by the recent work done on SPI NANDs.

Peter has proposed a SPI NAND controller interface (based on my
suggestions) where the controller had to implement hooks that the SPI
NAND framework could use to execute SPI NAND operations. I thought the
approach was acceptable as a first step before we move to a solution
where we share even more logic between SPI NOR and SPI NAND frameworks.
But I recently changed my mind after working with Frieder to support
his SPI NAND (Frieder has a SPI NAND connected to the FSL QSPI
controller).

And here come the problems: we definitely want one FSL QSPI driver
that works for both NANDs and NORs. In the current state, that means
forcing the FSL QSPI driver to implement both the SPI NAND and SPI NOR
interfaces. When you look at the internal controller logic, if we want
to support this without duplicating too much code, we have to add
an intermediate representation that abstracts away the NOR/NAND
specificities and pass only basic information to the controller:

* the opcode
* the number of address cycles
* the number of dummy cycles
* the number of data bytes to receive/transmit

And the same problem is likely to hit most of the controller drivers
placed in drivers/mtd/spi-nor.

Another problem I see with the current approach is the duplication
of DT parsing code in almost all drivers, even though most of them
follow the generic SPI controller/device bindings.

And finally, there's the confusion brought by the 2 entry points
exposed by the SPI NOR layer (and that we had planned to mimic in the
SPI NAND layer): you can register a SPI NOR device directly from the
dedicated SPI memory controller, or it can be registered through the
SPI layer if the SPI controller is a generic SPI controller. While
the generic SPI controller path works fine, the dedicated SPI NOR
controller path brings its own set of issues:

* the SPI bus is not represented in sysfs
* because there's no bus, there's no uevent, which means you'll have to
  select both the SPI NAND and SPI NOR logic as soon as one driver
  supports both interfaces if you don't want to run into loading
  dependency issues

Not to mention that, in order to work properly, the SPI flash ids may
have to be exported so that all QSPI controller drivers can use the
same database.

For all these reasons, I decided to try a new approach: move the
generic "SPI memory" logic in the SPI framework (what I call the SPI
memory interface here is actually not limited to SPI memories, and
I'm pretty sure other QSPI devices use the same kind of protocol).

So, basically, all kind of operations that can be done on a SPI
memory are encoded with a  CMD[+ADDR][+DUMMY][+DATA_{IN,OUT}] sequence,
and that's pretty much what I'm adding here: a way to ask the SPI
framework to execute a SPI memory operation. If the SPI controller
this memory is connected to is a generic SPI controller, then those
operations will be converted to generic SPI messages, but, if the
controller has an high-level interface to send those operations, then
it can expose it and the framework will use it.

One last comment: this new interface makes the spi_flash_read() API
redundant, because it's able to handle both read/write path and allows
the same kind of optimizations you could achieve with the
spi_flash_read hooks.

To sum-up: the idea behind this series is to converge to a
common/generic interface that could be used by any kind of SPI memory
driver and still allow per-controller optimizations.

Right now, the design is quite simple, and drivers can easily implement
the various mem_ops to optimize things. I'm currently working on the
conversion of the FSL driver, but I'm pretty sure most of the QSPI
drivers lying in drivers/mtd/spi-nor can easily be converted to this
new interface.

Once, we'll have that done, the SPI NOR layer will declare itself as
a regular spi_mem_driver and we'll be done with all the funky things
we have there. The SPI NAND patches have already been converted to this
approach.

If you want to see the big picture, my development branch is here [1]
and contains this series + the SPI NAND patches + a hack-ish FSL
QSPI ported to this driver which will hopefully support both SPI NANDs
and SPI NORs (only tested with a SPI NAND so far).

I know it's a big introduction, and I probably have lost most of my
readers before this point :-). But I'd really like to have feedback on
this series, because I'd hate to have to add yet another layer of
insane hacks in existing QSPI drivers to support both NORs and NANDs.

Mark, Cyrille, Marek, and all people that have been involved in SPI-NOR
development lately, please have a quick look at this RFC, and let me
know what you think.

Thanks,

Boris

[1]https://github.com/bbrezillon/linux/tree/spi-mem

Boris Brezillon (6):
  spi: Extend the core to ease integration of SPI memory controllers
  spi: bcm-qspi: Implement the spi_mem interface
  spi: bcm53xx: Implement the spi_mem interface
  spi: ti-qspi: Implement the spi_mem interface
  mtd: spi-nor: Use the spi_mem_xx() API
  spi: Get rid of the spi_flash_read() API

 drivers/mtd/devices/m25p80.c | 240 ++++++++--------------
 drivers/spi/spi-bcm-qspi.c   | 173 ++++++++--------
 drivers/spi/spi-bcm53xx.c    |  38 +++-
 drivers/spi/spi-ti-qspi.c    |  86 +++++---
 drivers/spi/spi.c            | 480 +++++++++++++++++++++++++++++++++++++------
 include/linux/spi/spi.h      | 263 ++++++++++++++++++++----
 6 files changed, 898 insertions(+), 382 deletions(-)

-- 
2.14.1

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

* [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-05 23:21 ` Boris Brezillon
@ 2018-02-05 23:21     ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

Some controllers are exposing high-level interfaces to access various
kind of SPI memories. Unfortunately they do not fit in the current
spi_controller model and usually have drivers placed in
drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
memories in general.

This is an attempt at defining a SPI memory interface which works for
all kinds of SPI memories (NORs, NANDs, SRAMs).

Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/spi/spi.c       | 423 +++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/spi/spi.h | 226 ++++++++++++++++++++++++++
 2 files changed, 646 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index b33a727a0158..57bc540a0521 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -2057,6 +2057,24 @@ static int of_spi_register_master(struct spi_controller *ctlr)
 }
 #endif
 
+static int spi_controller_check_ops(struct spi_controller *ctlr)
+{
+	/*
+	 * The controller can implement only the high-level SPI-memory like
+	 * operations if it does not support regular SPI transfers.
+	 */
+	if (ctlr->mem_ops) {
+		if (!ctlr->mem_ops->supports_op ||
+		    !ctlr->mem_ops->exec_op)
+			return -EINVAL;
+	} else if (!ctlr->transfer && !ctlr->transfer_one &&
+		   !ctlr->transfer_one_message) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 /**
  * spi_register_controller - register SPI master or slave controller
  * @ctlr: initialized master, originally from spi_alloc_master() or
@@ -2090,6 +2108,14 @@ int spi_register_controller(struct spi_controller *ctlr)
 	if (!dev)
 		return -ENODEV;
 
+	/*
+	 * Make sure all necessary hooks are implemented before registering
+	 * the SPI controller.
+	 */
+	status = spi_controller_check_ops(ctlr);
+	if (status)
+		return status;
+
 	if (!spi_controller_is_slave(ctlr)) {
 		status = of_spi_register_master(ctlr);
 		if (status)
@@ -2155,10 +2181,14 @@ int spi_register_controller(struct spi_controller *ctlr)
 			spi_controller_is_slave(ctlr) ? "slave" : "master",
 			dev_name(&ctlr->dev));
 
-	/* If we're using a queued driver, start the queue */
-	if (ctlr->transfer)
+	/*
+	 * If we're using a queued driver, start the queue. Note that we don't
+	 * need the queueing logic if the driver is only supporting high-level
+	 * memory operations.
+	 */
+	if (ctlr->transfer) {
 		dev_info(dev, "controller is unqueued, this is deprecated\n");
-	else {
+	} else if (ctlr->transfer_one || ctlr->transfer_one_message) {
 		status = spi_controller_initialize_queue(ctlr);
 		if (status) {
 			device_del(&ctlr->dev);
@@ -2893,6 +2923,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
 {
 	struct spi_controller *ctlr = spi->controller;
 
+	/*
+	 * Some controllers do not support doing regular SPI transfers. Return
+	 * ENOTSUPP when this is the case.
+	 */
+	if (!ctlr->transfer)
+		return -ENOTSUPP;
+
 	message->spi = spi;
 
 	SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_async);
@@ -3321,6 +3358,386 @@ int spi_write_then_read(struct spi_device *spi,
 }
 EXPORT_SYMBOL_GPL(spi_write_then_read);
 
+/**
+ * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a
+ *					  memory operation
+ * @ctlr: the SPI controller requesting this dma_map()
+ * @op: the memory operation containing the buffer to map
+ * @sgt: a pointer to a non-initialized sg_table that will be filled by this
+ *	 function
+ *
+ * Some controllers might want to do DMA on the data buffer embedded in @op.
+ * This helper prepares everything for you and provides a ready-to-use
+ * sg_table. This function is not intended to be called from spi drivers.
+ * Only SPI controller drivers should use it.
+ * Note that the caller must ensure the memory region pointed by
+ * op->data.buf.{in,out} is DMA-able before calling this function.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
+				       const struct spi_mem_op *op,
+				       struct sg_table *sgt)
+{
+	struct device *dmadev;
+
+	if (!op->data.nbytes)
+		return -EINVAL;
+
+	if (op->data.dir == SPI_MEM_DATA_OUT)
+		dmadev = ctlr->dma_tx ?
+			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
+	else
+		dmadev = ctlr->dma_rx ?
+			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
+
+	if (!dmadev)
+		return -EINVAL;
+
+	return spi_map_buf(ctlr, dmadev, sgt, op->data.buf.in, op->data.nbytes,
+			   op->data.dir == SPI_MEM_DATA_IN ?
+			   DMA_FROM_DEVICE : DMA_TO_DEVICE);
+}
+EXPORT_SYMBOL_GPL(spi_controller_dma_map_mem_op_data);
+
+/**
+ * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a
+ *					    memory operation
+ * @ctlr: the SPI controller requesting this dma_unmap()
+ * @op: the memory operation containing the buffer to unmap
+ * @sgt: a pointer to an sg_table previously initialized by
+ *	 spi_controller_dma_map_mem_op_data()
+ *
+ * Some controllers might want to do DMA on the data buffer embedded in @op.
+ * This helper prepares things so that the CPU can access the
+ * op->data.buf.{in,out} buffer again.
+ *
+ * This function is not intended to be called from spi drivers. Only SPI
+ * controller drivers should use it.
+ *
+ * This function should be called after the DMA operation has finished an is
+ * only valid if the previous spi_controller_dma_map_mem_op_data() has returned
+ * 0.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
+					  const struct spi_mem_op *op,
+					  struct sg_table *sgt)
+{
+	struct device *dmadev;
+
+	if (!op->data.nbytes)
+		return;
+
+	if (op->data.dir == SPI_MEM_DATA_OUT)
+		dmadev = ctlr->dma_tx ?
+			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
+	else
+		dmadev = ctlr->dma_rx ?
+			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
+
+	spi_unmap_buf(ctlr, dmadev, sgt,
+		      op->data.dir == SPI_MEM_DATA_IN ?
+		      DMA_FROM_DEVICE : DMA_TO_DEVICE);
+}
+EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data);
+
+static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
+{
+	u32 mode = mem->spi->mode;
+
+	switch (buswidth) {
+	case 1:
+		return 0;
+
+	case 2:
+		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
+		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
+			return 0;
+
+		break;
+
+	case 4:
+		if ((tx && (mode & SPI_TX_QUAD)) ||
+		    (!tx && (mode & SPI_RX_QUAD)))
+			return 0;
+
+		break;
+
+	default:
+		break;
+	}
+
+	return -ENOTSUPP;
+}
+
+/**
+ * spi_mem_supports_op() - Check if a memory device and the controller it is
+ *			   connected to support a specific memory operation
+ * @mem: the SPI memory
+ * @op: the memory operation to check
+ *
+ * Some controllers are only supporting Single or Dual IOs, others might only
+ * support specific opcodes, or it can even be that the controller and device
+ * both support Quad IOs but the hardware prevents you from using it because
+ * only 2 IO lines are connected.
+ *
+ * This function checks whether a specific operation is supported.
+ *
+ * Return: true if @op is supported, false otherwise.
+ */
+bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+	struct spi_controller *ctlr = mem->spi->controller;
+
+	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
+		return false;
+
+	if (op->addr.nbytes &&
+	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
+		return false;
+
+	if (op->dummy.nbytes &&
+	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
+		return false;
+
+	if (op->data.nbytes &&
+	    spi_check_buswidth_req(mem, op->data.buswidth,
+				   op->data.dir == SPI_MEM_DATA_IN ?
+				   false : true))
+		return false;
+
+	if (ctlr->mem_ops)
+		return ctlr->mem_ops->supports_op(mem, op);
+
+	return true;
+}
+EXPORT_SYMBOL_GPL(spi_mem_supports_op);
+
+/**
+ * spi_mem_exec_op() - Execute a memory operation
+ * @mem: the SPI memory
+ * @op: the memory operation to execute
+ *
+ * Executes a memory operation.
+ *
+ * This function first checks that @op is supported and then tries to execute
+ * it.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
+	struct spi_controller *ctlr = mem->spi->controller;
+	struct spi_transfer xfers[4] = { };
+	struct spi_message msg;
+	u8 *tmpbuf;
+	int ret;
+
+	if (!spi_mem_supports_op(mem, op))
+		return -ENOTSUPP;
+
+	if (ctlr->mem_ops) {
+		if (ctlr->auto_runtime_pm) {
+			ret = pm_runtime_get_sync(ctlr->dev.parent);
+			if (ret < 0) {
+				dev_err(&ctlr->dev,
+					"Failed to power device: %d\n",
+					ret);
+				return ret;
+			}
+		}
+
+		mutex_lock(&ctlr->bus_lock_mutex);
+		mutex_lock(&ctlr->io_mutex);
+		ret = ctlr->mem_ops->exec_op(mem, op);
+		mutex_unlock(&ctlr->io_mutex);
+		mutex_unlock(&ctlr->bus_lock_mutex);
+
+		if (ctlr->auto_runtime_pm)
+			pm_runtime_put(ctlr->dev.parent);
+
+		/*
+		 * Some controllers only optimize specific paths (typically the
+		 * read path) and expect the core to use the regular SPI
+		 * interface in these cases.
+		 */
+		if (!ret || ret != -ENOTSUPP)
+			return ret;
+	}
+
+	tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
+		     op->dummy.nbytes;
+
+	/*
+	 * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
+	 * we're guaranteed that this buffer is DMA-able, as required by the
+	 * SPI layer.
+	 */
+	tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
+	if (!tmpbuf)
+		return -ENOMEM;
+
+	spi_message_init(&msg);
+
+	tmpbuf[0] = op->cmd.opcode;
+	xfers[xferpos].tx_buf = tmpbuf;
+	xfers[xferpos].len = sizeof(op->cmd.opcode);
+	xfers[xferpos].tx_nbits = op->cmd.buswidth;
+	spi_message_add_tail(&xfers[xferpos], &msg);
+	xferpos++;
+	totalxferlen++;
+
+	if (op->addr.nbytes) {
+		memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);
+		xfers[xferpos].tx_buf = tmpbuf + 1;
+		xfers[xferpos].len = op->addr.nbytes;
+		xfers[xferpos].tx_nbits = op->addr.buswidth;
+		spi_message_add_tail(&xfers[xferpos], &msg);
+		xferpos++;
+		totalxferlen += op->addr.nbytes;
+	}
+
+	if (op->dummy.nbytes) {
+		memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes);
+		xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
+		xfers[xferpos].len = op->dummy.nbytes;
+		xfers[xferpos].tx_nbits = op->dummy.buswidth;
+		spi_message_add_tail(&xfers[xferpos], &msg);
+		xferpos++;
+		totalxferlen += op->dummy.nbytes;
+	}
+
+	if (op->data.nbytes) {
+		if (op->data.dir == SPI_MEM_DATA_IN) {
+			xfers[xferpos].rx_buf = op->data.buf.in;
+			xfers[xferpos].rx_nbits = op->data.buswidth;
+		} else {
+			xfers[xferpos].tx_buf = op->data.buf.out;
+			xfers[xferpos].tx_nbits = op->data.buswidth;
+		}
+
+		xfers[xferpos].len = op->data.nbytes;
+		spi_message_add_tail(&xfers[xferpos], &msg);
+		xferpos++;
+		totalxferlen += op->data.nbytes;
+	}
+
+	ret = spi_sync(mem->spi, &msg);
+
+	kfree(tmpbuf);
+
+	if (ret)
+		return ret;
+
+	if (msg.actual_length != totalxferlen)
+		return -EIO;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_mem_exec_op);
+
+/**
+ * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
+ *			      match controller limitations
+ * @mem: the SPI memory
+ * @op: the operation to adjust
+ *
+ * Some controllers have FIFO limitations and must split a data transfer
+ * operation into multiple ones, others require a specific alignment for
+ * optimized accesses. This function allows SPI mem drivers to split a single
+ * operation into multiple sub-operations when required.
+ *
+ * Return: a negative error code if the controller can't properly adjust @op,
+ *	   0 otherwise. Note that @op->data.nbytes will be updated if @op
+ *	   can't be handled in a single step.
+ */
+int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+	struct spi_controller *ctlr = mem->spi->controller;
+
+	if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
+		return ctlr->mem_ops->adjust_op_size(mem, op);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
+
+static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
+{
+	return container_of(drv, struct spi_mem_driver, spidrv.driver);
+}
+
+static int spi_mem_probe(struct spi_device *spi)
+{
+	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
+	struct spi_mem *mem;
+
+	mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL);
+	if (!mem)
+		return -ENOMEM;
+
+	mem->spi = spi;
+	spi_set_drvdata(spi, mem);
+
+	return memdrv->probe(mem);
+}
+
+static int spi_mem_remove(struct spi_device *spi)
+{
+	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
+	struct spi_mem *mem = spi_get_drvdata(spi);
+
+	if (memdrv->remove)
+		return memdrv->remove(mem);
+
+	return 0;
+}
+
+static void spi_mem_shutdown(struct spi_device *spi)
+{
+	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
+	struct spi_mem *mem = spi_get_drvdata(spi);
+
+	if (memdrv->shutdown)
+		memdrv->shutdown(mem);
+}
+
+/**
+ * spi_mem_driver_register_with_owner() - Register a SPI memory driver
+ * @memdrv: the SPI memory driver to register
+ * @owner: the owner of this driver
+ *
+ * Registers a SPI memory driver.
+ *
+ * Return: 0 in case of success, a negative error core otherwise.
+ */
+
+int spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv,
+				       struct module *owner)
+{
+	memdrv->spidrv.probe = spi_mem_probe;
+	memdrv->spidrv.remove = spi_mem_remove;
+	memdrv->spidrv.shutdown = spi_mem_shutdown;
+
+	return __spi_register_driver(owner, &memdrv->spidrv);
+}
+EXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner);
+
+/**
+ * spi_mem_driver_unregister_with_owner() - Unregister a SPI memory driver
+ * @memdrv: the SPI memory driver to unregister
+ *
+ * Unregisters a SPI memory driver.
+ */
+void spi_mem_driver_unregister(struct spi_mem_driver *memdrv)
+{
+	spi_unregister_driver(&memdrv->spidrv);
+}
+EXPORT_SYMBOL_GPL(spi_mem_driver_unregister);
+
 /*-------------------------------------------------------------------------*/
 
 #if IS_ENABLED(CONFIG_OF_DYNAMIC)
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 7b2170bfd6e7..af3c4ac62b55 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -27,6 +27,7 @@ struct property_entry;
 struct spi_controller;
 struct spi_transfer;
 struct spi_flash_read_message;
+struct spi_controller_mem_ops;
 
 /*
  * INTERFACES between SPI master-side drivers and SPI slave protocol handlers,
@@ -376,6 +377,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
  *                    transfer_one callback.
  * @handle_err: the subsystem calls the driver to handle an error that occurs
  *		in the generic implementation of transfer_one_message().
+ * @mem_ops: optimized/dedicated operations for interactions with SPI memory.
+ *	     This field is optional and should only be implemented if the
+ *	     controller has native support for memory like operations.
  * @unprepare_message: undo any work done by prepare_message().
  * @slave_abort: abort the ongoing transfer request on an SPI slave controller
  * @spi_flash_read: to support spi-controller hardwares that provide
@@ -564,6 +568,9 @@ struct spi_controller {
 	void (*handle_err)(struct spi_controller *ctlr,
 			   struct spi_message *message);
 
+	/* Optimized handlers for SPI memory-like operations. */
+	const struct spi_controller_mem_ops *mem_ops;
+
 	/* gpio chip select */
 	int			*cs_gpios;
 
@@ -1227,6 +1234,225 @@ int spi_flash_read(struct spi_device *spi,
 
 /*---------------------------------------------------------------------------*/
 
+/* SPI memory related definitions. */
+
+#define SPI_MEM_OP_CMD(__opcode, __buswidth)			\
+	{							\
+		.buswidth = __buswidth,				\
+		.opcode = __opcode,				\
+	}
+
+#define SPI_MEM_OP_ADDRS(__nbytes, __buf, __buswidth)		\
+	{							\
+		.nbytes = __nbytes,				\
+		.buf = __buf,					\
+		.buswidth = __buswidth,				\
+	}
+
+#define SPI_MEM_OP_NO_ADDRS	{ }
+
+#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth)			\
+	{							\
+		.nbytes = __nbytes,				\
+		.buswidth = __buswidth,				\
+	}
+
+#define SPI_MEM_OP_NO_DUMMY	{ }
+
+#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth)		\
+	{							\
+		.dir = SPI_MEM_DATA_IN,				\
+		.nbytes = __nbytes,				\
+		.buf.in = __buf,				\
+		.buswidth = __buswidth,				\
+	}
+
+#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth)	\
+	{							\
+		.dir = SPI_MEM_DATA_OUT,			\
+		.nbytes = __nbytes,				\
+		.buf.out = __buf,				\
+		.buswidth = __buswidth,				\
+	}
+
+#define SPI_MEM_OP_NO_DATA	{ }
+
+/**
+ * enum spi_mem_data_dir - describes the direction of a SPI memory data
+ *			   transfer from the controller perspective
+ * @SPI_MEM_DATA_IN: data coming from the SPI memory
+ * @SPI_MEM_DATA_OUT: data sent the SPI memory
+ */
+enum spi_mem_data_dir {
+	SPI_MEM_DATA_IN,
+	SPI_MEM_DATA_OUT,
+};
+
+/**
+ * struct spi_mem_op - describes a SPI memory operation
+ * @cmd.buswidth: number of IO lines used to transmit the command
+ * @cmd.opcode: operation opcode
+ * @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.buf: buffer storing the address bytes
+ * @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
+ * @data.buswidth: number of IO lanes used to send/receive the data
+ * @data.dir: direction of the transfer
+ * @data.buf.in: input buffer
+ * @data.buf.out: output buffer
+ */
+struct spi_mem_op {
+	struct {
+		u8 buswidth;
+		u8 opcode;
+	} cmd;
+
+	struct {
+		u8 nbytes;
+		u8 buswidth;
+		const u8 *buf;
+	} addr;
+
+	struct {
+		u8 nbytes;
+		u8 buswidth;
+	} dummy;
+
+	struct {
+		u8 buswidth;
+		enum spi_mem_data_dir dir;
+		unsigned int nbytes;
+		/* buf.{in,out} must be DMA-able. */
+		union {
+			void *in;
+			const void *out;
+		} buf;
+	} data;
+};
+
+#define SPI_MEM_OP(__cmd, __addr, __dummy, __data)		\
+	{							\
+		.cmd = __cmd,					\
+		.addr = __addr,					\
+		.dummy = __dummy,				\
+		.data = __data,					\
+	}
+
+/**
+ * struct spi_mem - describes a SPI memory device
+ * @spi: the underlying SPI device
+ * @drvpriv: spi_mem_drviver private data
+ *
+ * Extra information that describe the SPI memory device and may be needed by
+ * the controller to properly handle this device should be placed here.
+ *
+ * One example would be the device size since some controller expose their SPI
+ * mem devices through a io-mapped region.
+ */
+struct spi_mem {
+	struct spi_device *spi;
+	void *drvpriv;
+};
+
+/**
+ * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
+ *				  device
+ * @mem: memory device
+ * @data: data to attach to the memory device
+ */
+static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
+{
+	mem->drvpriv = data;
+}
+
+/**
+ * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
+ *				  device
+ * @mem: memory device
+ *
+ * Return: the data attached to the mem device.
+ */
+static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
+{
+	return mem->drvpriv;
+}
+
+/**
+ * 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)
+ * @supports_op: check if an operation is supported by the controller
+ * @exec_op: execute a SPI memory operation
+ *
+ * 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.
+ */
+struct spi_controller_mem_ops {
+	int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
+	bool (*supports_op)(struct spi_mem *mem,
+			    const struct spi_mem_op *op);
+	int (*exec_op)(struct spi_mem *mem,
+		       const struct spi_mem_op *op);
+};
+
+int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
+				       const struct spi_mem_op *op,
+				       struct sg_table *sg);
+
+void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
+					  const struct spi_mem_op *op,
+					  struct sg_table *sg);
+
+int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
+
+bool spi_mem_supports_op(struct spi_mem *mem,
+			 const struct spi_mem_op *op);
+
+int spi_mem_exec_op(struct spi_mem *mem,
+		    const struct spi_mem_op *op);
+
+/**
+ * struct spi_mem_driver - SPI memory driver
+ * @spidrv: inherit from a SPI driver
+ * @probe: probe a SPI memory. Usually where detection/initialization takes
+ *	   place
+ * @remove: remove a SPI memory
+ * @shutdown: take appropriate action when the system is shutdown
+ *
+ * This is just a thin wrapper around a spi_driver. The core takes care of
+ * allocating the spi_mem object and forwarding the probe/remove/shutdown
+ * request to the spi_mem_driver. The reason we use this wrapper is because
+ * we might have to stuff more information into the spi_mem struct to let
+ * SPI controllers know more about the SPI memory they interact with, and
+ * having this intermediate layer allows us to do that without adding more
+ * useless fields to the spi_device object.
+ */
+struct spi_mem_driver {
+	struct spi_driver spidrv;
+	int (*probe)(struct spi_mem *mem);
+	int (*remove)(struct spi_mem *mem);
+	void (*shutdown)(struct spi_mem *mem);
+};
+
+int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
+				       struct module *owner);
+
+#define spi_mem_driver_register(__drv)                                  \
+	spi_mem_driver_register_with_owner(__drv, THIS_MODULE)
+
+void spi_mem_driver_unregister(struct spi_mem_driver *drv);
+
+#define module_spi_mem_driver(__drv)                                    \
+	module_driver(__drv, spi_mem_driver_register,                   \
+		      spi_mem_driver_unregister)
+
+/*---------------------------------------------------------------------------*/
+
 /*
  * INTERFACE between board init code and SPI infrastructure.
  *
-- 
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-02-05 23:21     ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

From: Boris Brezillon <boris.brezillon@free-electrons.com>

Some controllers are exposing high-level interfaces to access various
kind of SPI memories. Unfortunately they do not fit in the current
spi_controller model and usually have drivers placed in
drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
memories in general.

This is an attempt at defining a SPI memory interface which works for
all kinds of SPI memories (NORs, NANDs, SRAMs).

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/spi/spi.c       | 423 +++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/spi/spi.h | 226 ++++++++++++++++++++++++++
 2 files changed, 646 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index b33a727a0158..57bc540a0521 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -2057,6 +2057,24 @@ static int of_spi_register_master(struct spi_controller *ctlr)
 }
 #endif
 
+static int spi_controller_check_ops(struct spi_controller *ctlr)
+{
+	/*
+	 * The controller can implement only the high-level SPI-memory like
+	 * operations if it does not support regular SPI transfers.
+	 */
+	if (ctlr->mem_ops) {
+		if (!ctlr->mem_ops->supports_op ||
+		    !ctlr->mem_ops->exec_op)
+			return -EINVAL;
+	} else if (!ctlr->transfer && !ctlr->transfer_one &&
+		   !ctlr->transfer_one_message) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 /**
  * spi_register_controller - register SPI master or slave controller
  * @ctlr: initialized master, originally from spi_alloc_master() or
@@ -2090,6 +2108,14 @@ int spi_register_controller(struct spi_controller *ctlr)
 	if (!dev)
 		return -ENODEV;
 
+	/*
+	 * Make sure all necessary hooks are implemented before registering
+	 * the SPI controller.
+	 */
+	status = spi_controller_check_ops(ctlr);
+	if (status)
+		return status;
+
 	if (!spi_controller_is_slave(ctlr)) {
 		status = of_spi_register_master(ctlr);
 		if (status)
@@ -2155,10 +2181,14 @@ int spi_register_controller(struct spi_controller *ctlr)
 			spi_controller_is_slave(ctlr) ? "slave" : "master",
 			dev_name(&ctlr->dev));
 
-	/* If we're using a queued driver, start the queue */
-	if (ctlr->transfer)
+	/*
+	 * If we're using a queued driver, start the queue. Note that we don't
+	 * need the queueing logic if the driver is only supporting high-level
+	 * memory operations.
+	 */
+	if (ctlr->transfer) {
 		dev_info(dev, "controller is unqueued, this is deprecated\n");
-	else {
+	} else if (ctlr->transfer_one || ctlr->transfer_one_message) {
 		status = spi_controller_initialize_queue(ctlr);
 		if (status) {
 			device_del(&ctlr->dev);
@@ -2893,6 +2923,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
 {
 	struct spi_controller *ctlr = spi->controller;
 
+	/*
+	 * Some controllers do not support doing regular SPI transfers. Return
+	 * ENOTSUPP when this is the case.
+	 */
+	if (!ctlr->transfer)
+		return -ENOTSUPP;
+
 	message->spi = spi;
 
 	SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_async);
@@ -3321,6 +3358,386 @@ int spi_write_then_read(struct spi_device *spi,
 }
 EXPORT_SYMBOL_GPL(spi_write_then_read);
 
+/**
+ * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a
+ *					  memory operation
+ * @ctlr: the SPI controller requesting this dma_map()
+ * @op: the memory operation containing the buffer to map
+ * @sgt: a pointer to a non-initialized sg_table that will be filled by this
+ *	 function
+ *
+ * Some controllers might want to do DMA on the data buffer embedded in @op.
+ * This helper prepares everything for you and provides a ready-to-use
+ * sg_table. This function is not intended to be called from spi drivers.
+ * Only SPI controller drivers should use it.
+ * Note that the caller must ensure the memory region pointed by
+ * op->data.buf.{in,out} is DMA-able before calling this function.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
+				       const struct spi_mem_op *op,
+				       struct sg_table *sgt)
+{
+	struct device *dmadev;
+
+	if (!op->data.nbytes)
+		return -EINVAL;
+
+	if (op->data.dir == SPI_MEM_DATA_OUT)
+		dmadev = ctlr->dma_tx ?
+			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
+	else
+		dmadev = ctlr->dma_rx ?
+			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
+
+	if (!dmadev)
+		return -EINVAL;
+
+	return spi_map_buf(ctlr, dmadev, sgt, op->data.buf.in, op->data.nbytes,
+			   op->data.dir == SPI_MEM_DATA_IN ?
+			   DMA_FROM_DEVICE : DMA_TO_DEVICE);
+}
+EXPORT_SYMBOL_GPL(spi_controller_dma_map_mem_op_data);
+
+/**
+ * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a
+ *					    memory operation
+ * @ctlr: the SPI controller requesting this dma_unmap()
+ * @op: the memory operation containing the buffer to unmap
+ * @sgt: a pointer to an sg_table previously initialized by
+ *	 spi_controller_dma_map_mem_op_data()
+ *
+ * Some controllers might want to do DMA on the data buffer embedded in @op.
+ * This helper prepares things so that the CPU can access the
+ * op->data.buf.{in,out} buffer again.
+ *
+ * This function is not intended to be called from spi drivers. Only SPI
+ * controller drivers should use it.
+ *
+ * This function should be called after the DMA operation has finished an is
+ * only valid if the previous spi_controller_dma_map_mem_op_data() has returned
+ * 0.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
+					  const struct spi_mem_op *op,
+					  struct sg_table *sgt)
+{
+	struct device *dmadev;
+
+	if (!op->data.nbytes)
+		return;
+
+	if (op->data.dir == SPI_MEM_DATA_OUT)
+		dmadev = ctlr->dma_tx ?
+			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
+	else
+		dmadev = ctlr->dma_rx ?
+			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
+
+	spi_unmap_buf(ctlr, dmadev, sgt,
+		      op->data.dir == SPI_MEM_DATA_IN ?
+		      DMA_FROM_DEVICE : DMA_TO_DEVICE);
+}
+EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data);
+
+static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
+{
+	u32 mode = mem->spi->mode;
+
+	switch (buswidth) {
+	case 1:
+		return 0;
+
+	case 2:
+		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
+		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
+			return 0;
+
+		break;
+
+	case 4:
+		if ((tx && (mode & SPI_TX_QUAD)) ||
+		    (!tx && (mode & SPI_RX_QUAD)))
+			return 0;
+
+		break;
+
+	default:
+		break;
+	}
+
+	return -ENOTSUPP;
+}
+
+/**
+ * spi_mem_supports_op() - Check if a memory device and the controller it is
+ *			   connected to support a specific memory operation
+ * @mem: the SPI memory
+ * @op: the memory operation to check
+ *
+ * Some controllers are only supporting Single or Dual IOs, others might only
+ * support specific opcodes, or it can even be that the controller and device
+ * both support Quad IOs but the hardware prevents you from using it because
+ * only 2 IO lines are connected.
+ *
+ * This function checks whether a specific operation is supported.
+ *
+ * Return: true if @op is supported, false otherwise.
+ */
+bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+	struct spi_controller *ctlr = mem->spi->controller;
+
+	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
+		return false;
+
+	if (op->addr.nbytes &&
+	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
+		return false;
+
+	if (op->dummy.nbytes &&
+	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
+		return false;
+
+	if (op->data.nbytes &&
+	    spi_check_buswidth_req(mem, op->data.buswidth,
+				   op->data.dir == SPI_MEM_DATA_IN ?
+				   false : true))
+		return false;
+
+	if (ctlr->mem_ops)
+		return ctlr->mem_ops->supports_op(mem, op);
+
+	return true;
+}
+EXPORT_SYMBOL_GPL(spi_mem_supports_op);
+
+/**
+ * spi_mem_exec_op() - Execute a memory operation
+ * @mem: the SPI memory
+ * @op: the memory operation to execute
+ *
+ * Executes a memory operation.
+ *
+ * This function first checks that @op is supported and then tries to execute
+ * it.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
+	struct spi_controller *ctlr = mem->spi->controller;
+	struct spi_transfer xfers[4] = { };
+	struct spi_message msg;
+	u8 *tmpbuf;
+	int ret;
+
+	if (!spi_mem_supports_op(mem, op))
+		return -ENOTSUPP;
+
+	if (ctlr->mem_ops) {
+		if (ctlr->auto_runtime_pm) {
+			ret = pm_runtime_get_sync(ctlr->dev.parent);
+			if (ret < 0) {
+				dev_err(&ctlr->dev,
+					"Failed to power device: %d\n",
+					ret);
+				return ret;
+			}
+		}
+
+		mutex_lock(&ctlr->bus_lock_mutex);
+		mutex_lock(&ctlr->io_mutex);
+		ret = ctlr->mem_ops->exec_op(mem, op);
+		mutex_unlock(&ctlr->io_mutex);
+		mutex_unlock(&ctlr->bus_lock_mutex);
+
+		if (ctlr->auto_runtime_pm)
+			pm_runtime_put(ctlr->dev.parent);
+
+		/*
+		 * Some controllers only optimize specific paths (typically the
+		 * read path) and expect the core to use the regular SPI
+		 * interface in these cases.
+		 */
+		if (!ret || ret != -ENOTSUPP)
+			return ret;
+	}
+
+	tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
+		     op->dummy.nbytes;
+
+	/*
+	 * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
+	 * we're guaranteed that this buffer is DMA-able, as required by the
+	 * SPI layer.
+	 */
+	tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
+	if (!tmpbuf)
+		return -ENOMEM;
+
+	spi_message_init(&msg);
+
+	tmpbuf[0] = op->cmd.opcode;
+	xfers[xferpos].tx_buf = tmpbuf;
+	xfers[xferpos].len = sizeof(op->cmd.opcode);
+	xfers[xferpos].tx_nbits = op->cmd.buswidth;
+	spi_message_add_tail(&xfers[xferpos], &msg);
+	xferpos++;
+	totalxferlen++;
+
+	if (op->addr.nbytes) {
+		memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);
+		xfers[xferpos].tx_buf = tmpbuf + 1;
+		xfers[xferpos].len = op->addr.nbytes;
+		xfers[xferpos].tx_nbits = op->addr.buswidth;
+		spi_message_add_tail(&xfers[xferpos], &msg);
+		xferpos++;
+		totalxferlen += op->addr.nbytes;
+	}
+
+	if (op->dummy.nbytes) {
+		memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes);
+		xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
+		xfers[xferpos].len = op->dummy.nbytes;
+		xfers[xferpos].tx_nbits = op->dummy.buswidth;
+		spi_message_add_tail(&xfers[xferpos], &msg);
+		xferpos++;
+		totalxferlen += op->dummy.nbytes;
+	}
+
+	if (op->data.nbytes) {
+		if (op->data.dir == SPI_MEM_DATA_IN) {
+			xfers[xferpos].rx_buf = op->data.buf.in;
+			xfers[xferpos].rx_nbits = op->data.buswidth;
+		} else {
+			xfers[xferpos].tx_buf = op->data.buf.out;
+			xfers[xferpos].tx_nbits = op->data.buswidth;
+		}
+
+		xfers[xferpos].len = op->data.nbytes;
+		spi_message_add_tail(&xfers[xferpos], &msg);
+		xferpos++;
+		totalxferlen += op->data.nbytes;
+	}
+
+	ret = spi_sync(mem->spi, &msg);
+
+	kfree(tmpbuf);
+
+	if (ret)
+		return ret;
+
+	if (msg.actual_length != totalxferlen)
+		return -EIO;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_mem_exec_op);
+
+/**
+ * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
+ *			      match controller limitations
+ * @mem: the SPI memory
+ * @op: the operation to adjust
+ *
+ * Some controllers have FIFO limitations and must split a data transfer
+ * operation into multiple ones, others require a specific alignment for
+ * optimized accesses. This function allows SPI mem drivers to split a single
+ * operation into multiple sub-operations when required.
+ *
+ * Return: a negative error code if the controller can't properly adjust @op,
+ *	   0 otherwise. Note that @op->data.nbytes will be updated if @op
+ *	   can't be handled in a single step.
+ */
+int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+	struct spi_controller *ctlr = mem->spi->controller;
+
+	if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
+		return ctlr->mem_ops->adjust_op_size(mem, op);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
+
+static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
+{
+	return container_of(drv, struct spi_mem_driver, spidrv.driver);
+}
+
+static int spi_mem_probe(struct spi_device *spi)
+{
+	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
+	struct spi_mem *mem;
+
+	mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL);
+	if (!mem)
+		return -ENOMEM;
+
+	mem->spi = spi;
+	spi_set_drvdata(spi, mem);
+
+	return memdrv->probe(mem);
+}
+
+static int spi_mem_remove(struct spi_device *spi)
+{
+	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
+	struct spi_mem *mem = spi_get_drvdata(spi);
+
+	if (memdrv->remove)
+		return memdrv->remove(mem);
+
+	return 0;
+}
+
+static void spi_mem_shutdown(struct spi_device *spi)
+{
+	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
+	struct spi_mem *mem = spi_get_drvdata(spi);
+
+	if (memdrv->shutdown)
+		memdrv->shutdown(mem);
+}
+
+/**
+ * spi_mem_driver_register_with_owner() - Register a SPI memory driver
+ * @memdrv: the SPI memory driver to register
+ * @owner: the owner of this driver
+ *
+ * Registers a SPI memory driver.
+ *
+ * Return: 0 in case of success, a negative error core otherwise.
+ */
+
+int spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv,
+				       struct module *owner)
+{
+	memdrv->spidrv.probe = spi_mem_probe;
+	memdrv->spidrv.remove = spi_mem_remove;
+	memdrv->spidrv.shutdown = spi_mem_shutdown;
+
+	return __spi_register_driver(owner, &memdrv->spidrv);
+}
+EXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner);
+
+/**
+ * spi_mem_driver_unregister_with_owner() - Unregister a SPI memory driver
+ * @memdrv: the SPI memory driver to unregister
+ *
+ * Unregisters a SPI memory driver.
+ */
+void spi_mem_driver_unregister(struct spi_mem_driver *memdrv)
+{
+	spi_unregister_driver(&memdrv->spidrv);
+}
+EXPORT_SYMBOL_GPL(spi_mem_driver_unregister);
+
 /*-------------------------------------------------------------------------*/
 
 #if IS_ENABLED(CONFIG_OF_DYNAMIC)
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 7b2170bfd6e7..af3c4ac62b55 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -27,6 +27,7 @@ struct property_entry;
 struct spi_controller;
 struct spi_transfer;
 struct spi_flash_read_message;
+struct spi_controller_mem_ops;
 
 /*
  * INTERFACES between SPI master-side drivers and SPI slave protocol handlers,
@@ -376,6 +377,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
  *                    transfer_one callback.
  * @handle_err: the subsystem calls the driver to handle an error that occurs
  *		in the generic implementation of transfer_one_message().
+ * @mem_ops: optimized/dedicated operations for interactions with SPI memory.
+ *	     This field is optional and should only be implemented if the
+ *	     controller has native support for memory like operations.
  * @unprepare_message: undo any work done by prepare_message().
  * @slave_abort: abort the ongoing transfer request on an SPI slave controller
  * @spi_flash_read: to support spi-controller hardwares that provide
@@ -564,6 +568,9 @@ struct spi_controller {
 	void (*handle_err)(struct spi_controller *ctlr,
 			   struct spi_message *message);
 
+	/* Optimized handlers for SPI memory-like operations. */
+	const struct spi_controller_mem_ops *mem_ops;
+
 	/* gpio chip select */
 	int			*cs_gpios;
 
@@ -1227,6 +1234,225 @@ int spi_flash_read(struct spi_device *spi,
 
 /*---------------------------------------------------------------------------*/
 
+/* SPI memory related definitions. */
+
+#define SPI_MEM_OP_CMD(__opcode, __buswidth)			\
+	{							\
+		.buswidth = __buswidth,				\
+		.opcode = __opcode,				\
+	}
+
+#define SPI_MEM_OP_ADDRS(__nbytes, __buf, __buswidth)		\
+	{							\
+		.nbytes = __nbytes,				\
+		.buf = __buf,					\
+		.buswidth = __buswidth,				\
+	}
+
+#define SPI_MEM_OP_NO_ADDRS	{ }
+
+#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth)			\
+	{							\
+		.nbytes = __nbytes,				\
+		.buswidth = __buswidth,				\
+	}
+
+#define SPI_MEM_OP_NO_DUMMY	{ }
+
+#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth)		\
+	{							\
+		.dir = SPI_MEM_DATA_IN,				\
+		.nbytes = __nbytes,				\
+		.buf.in = __buf,				\
+		.buswidth = __buswidth,				\
+	}
+
+#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth)	\
+	{							\
+		.dir = SPI_MEM_DATA_OUT,			\
+		.nbytes = __nbytes,				\
+		.buf.out = __buf,				\
+		.buswidth = __buswidth,				\
+	}
+
+#define SPI_MEM_OP_NO_DATA	{ }
+
+/**
+ * enum spi_mem_data_dir - describes the direction of a SPI memory data
+ *			   transfer from the controller perspective
+ * @SPI_MEM_DATA_IN: data coming from the SPI memory
+ * @SPI_MEM_DATA_OUT: data sent the SPI memory
+ */
+enum spi_mem_data_dir {
+	SPI_MEM_DATA_IN,
+	SPI_MEM_DATA_OUT,
+};
+
+/**
+ * struct spi_mem_op - describes a SPI memory operation
+ * @cmd.buswidth: number of IO lines used to transmit the command
+ * @cmd.opcode: operation opcode
+ * @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.buf: buffer storing the address bytes
+ * @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
+ * @data.buswidth: number of IO lanes used to send/receive the data
+ * @data.dir: direction of the transfer
+ * @data.buf.in: input buffer
+ * @data.buf.out: output buffer
+ */
+struct spi_mem_op {
+	struct {
+		u8 buswidth;
+		u8 opcode;
+	} cmd;
+
+	struct {
+		u8 nbytes;
+		u8 buswidth;
+		const u8 *buf;
+	} addr;
+
+	struct {
+		u8 nbytes;
+		u8 buswidth;
+	} dummy;
+
+	struct {
+		u8 buswidth;
+		enum spi_mem_data_dir dir;
+		unsigned int nbytes;
+		/* buf.{in,out} must be DMA-able. */
+		union {
+			void *in;
+			const void *out;
+		} buf;
+	} data;
+};
+
+#define SPI_MEM_OP(__cmd, __addr, __dummy, __data)		\
+	{							\
+		.cmd = __cmd,					\
+		.addr = __addr,					\
+		.dummy = __dummy,				\
+		.data = __data,					\
+	}
+
+/**
+ * struct spi_mem - describes a SPI memory device
+ * @spi: the underlying SPI device
+ * @drvpriv: spi_mem_drviver private data
+ *
+ * Extra information that describe the SPI memory device and may be needed by
+ * the controller to properly handle this device should be placed here.
+ *
+ * One example would be the device size since some controller expose their SPI
+ * mem devices through a io-mapped region.
+ */
+struct spi_mem {
+	struct spi_device *spi;
+	void *drvpriv;
+};
+
+/**
+ * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
+ *				  device
+ * @mem: memory device
+ * @data: data to attach to the memory device
+ */
+static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
+{
+	mem->drvpriv = data;
+}
+
+/**
+ * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
+ *				  device
+ * @mem: memory device
+ *
+ * Return: the data attached to the mem device.
+ */
+static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
+{
+	return mem->drvpriv;
+}
+
+/**
+ * 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)
+ * @supports_op: check if an operation is supported by the controller
+ * @exec_op: execute a SPI memory operation
+ *
+ * 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.
+ */
+struct spi_controller_mem_ops {
+	int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
+	bool (*supports_op)(struct spi_mem *mem,
+			    const struct spi_mem_op *op);
+	int (*exec_op)(struct spi_mem *mem,
+		       const struct spi_mem_op *op);
+};
+
+int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
+				       const struct spi_mem_op *op,
+				       struct sg_table *sg);
+
+void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
+					  const struct spi_mem_op *op,
+					  struct sg_table *sg);
+
+int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
+
+bool spi_mem_supports_op(struct spi_mem *mem,
+			 const struct spi_mem_op *op);
+
+int spi_mem_exec_op(struct spi_mem *mem,
+		    const struct spi_mem_op *op);
+
+/**
+ * struct spi_mem_driver - SPI memory driver
+ * @spidrv: inherit from a SPI driver
+ * @probe: probe a SPI memory. Usually where detection/initialization takes
+ *	   place
+ * @remove: remove a SPI memory
+ * @shutdown: take appropriate action when the system is shutdown
+ *
+ * This is just a thin wrapper around a spi_driver. The core takes care of
+ * allocating the spi_mem object and forwarding the probe/remove/shutdown
+ * request to the spi_mem_driver. The reason we use this wrapper is because
+ * we might have to stuff more information into the spi_mem struct to let
+ * SPI controllers know more about the SPI memory they interact with, and
+ * having this intermediate layer allows us to do that without adding more
+ * useless fields to the spi_device object.
+ */
+struct spi_mem_driver {
+	struct spi_driver spidrv;
+	int (*probe)(struct spi_mem *mem);
+	int (*remove)(struct spi_mem *mem);
+	void (*shutdown)(struct spi_mem *mem);
+};
+
+int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
+				       struct module *owner);
+
+#define spi_mem_driver_register(__drv)                                  \
+	spi_mem_driver_register_with_owner(__drv, THIS_MODULE)
+
+void spi_mem_driver_unregister(struct spi_mem_driver *drv);
+
+#define module_spi_mem_driver(__drv)                                    \
+	module_driver(__drv, spi_mem_driver_register,                   \
+		      spi_mem_driver_unregister)
+
+/*---------------------------------------------------------------------------*/
+
 /*
  * INTERFACE between board init code and SPI infrastructure.
  *
-- 
2.14.1

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

* [RFC PATCH 2/6] spi: bcm-qspi: Implement the spi_mem interface
  2018-02-05 23:21 ` Boris Brezillon
@ 2018-02-05 23:21     ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

The spi_mem interface is meant to replace the ->spi_flash_read() one.
Implement the ->exec_op() method to ease removal of the old interface.

Not that ->spi_flash_read() is now implemented as a wrapper around the
new bcm_qspi_exec_mem_op() function so that we can easily get rid of
it when ->spi_flash_read() is removed.

Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/spi/spi-bcm-qspi.c | 205 +++++++++++++++++++++++++++------------------
 1 file changed, 124 insertions(+), 81 deletions(-)

diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c
index a172ab299e80..f47a13453ef6 100644
--- a/drivers/spi/spi-bcm-qspi.c
+++ b/drivers/spi/spi-bcm-qspi.c
@@ -215,10 +215,10 @@ struct bcm_qspi {
 	int bspi_maj_rev;
 	int bspi_min_rev;
 	int bspi_enabled;
-	struct spi_flash_read_message *bspi_rf_msg;
-	u32 bspi_rf_msg_idx;
-	u32 bspi_rf_msg_len;
-	u32 bspi_rf_msg_status;
+	const struct spi_mem_op *bspi_rf_op;
+	u32 bspi_rf_op_idx;
+	u32 bspi_rf_op_len;
+	u32 bspi_rf_op_status;
 	struct bcm_xfer_mode xfer_mode;
 	u32 s3_strap_override_ctrl;
 	bool bspi_mode;
@@ -313,26 +313,26 @@ static inline void bcm_qspi_bspi_lr_clear(struct bcm_qspi *qspi)
 
 static void bcm_qspi_bspi_lr_data_read(struct bcm_qspi *qspi)
 {
-	u32 *buf = (u32 *)qspi->bspi_rf_msg->buf;
+	u32 *buf = (u32 *)qspi->bspi_rf_op->data.buf.in;
 	u32 data = 0;
 
-	dev_dbg(&qspi->pdev->dev, "xfer %p rx %p rxlen %d\n", qspi->bspi_rf_msg,
-		qspi->bspi_rf_msg->buf, qspi->bspi_rf_msg_len);
+	dev_dbg(&qspi->pdev->dev, "xfer %p rx %p rxlen %d\n", qspi->bspi_rf_op,
+		qspi->bspi_rf_op->data.buf.in, qspi->bspi_rf_op_len);
 	while (!bcm_qspi_bspi_lr_is_fifo_empty(qspi)) {
 		data = bcm_qspi_bspi_lr_read_fifo(qspi);
-		if (likely(qspi->bspi_rf_msg_len >= 4) &&
+		if (likely(qspi->bspi_rf_op_len >= 4) &&
 		    IS_ALIGNED((uintptr_t)buf, 4)) {
-			buf[qspi->bspi_rf_msg_idx++] = data;
-			qspi->bspi_rf_msg_len -= 4;
+			buf[qspi->bspi_rf_op_idx++] = data;
+			qspi->bspi_rf_op_len -= 4;
 		} else {
 			/* Read out remaining bytes, make sure*/
-			u8 *cbuf = (u8 *)&buf[qspi->bspi_rf_msg_idx];
+			u8 *cbuf = (u8 *)&buf[qspi->bspi_rf_op_idx];
 
 			data = cpu_to_le32(data);
-			while (qspi->bspi_rf_msg_len) {
+			while (qspi->bspi_rf_op_len) {
 				*cbuf++ = (u8)data;
 				data >>= 8;
-				qspi->bspi_rf_msg_len--;
+				qspi->bspi_rf_op_len--;
 			}
 		}
 	}
@@ -349,14 +349,12 @@ static void bcm_qspi_bspi_set_xfer_params(struct bcm_qspi *qspi, u8 cmd_byte,
 }
 
 static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi,
-				       struct spi_flash_read_message *msg,
-				       int hp)
+				       const struct spi_mem_op *op, int hp)
 {
 	int bpc = 0, bpp = 0;
-	u8 command = msg->read_opcode;
-	int width  = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
-	int addrlen = msg->addr_width;
-	int addr_nbits = msg->addr_nbits ? msg->addr_nbits : SPI_NBITS_SINGLE;
+	u8 command = op->cmd.opcode;
+	int width  = op->cmd.buswidth ? op->cmd.buswidth : SPI_NBITS_SINGLE;
+	int addrlen = op->addr.nbytes * 8;
 	int flex_mode = 1;
 
 	dev_dbg(&qspi->pdev->dev, "set flex mode w %x addrlen %x hp %d\n",
@@ -365,7 +363,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi,
 	if (addrlen == BSPI_ADDRLEN_4BYTES)
 		bpp = BSPI_BPP_ADDR_SELECT_MASK;
 
-	bpp |= msg->dummy_bytes * (8/addr_nbits);
+	bpp |= (op->dummy.nbytes * 8) / op->dummy.buswidth;
 
 	switch (width) {
 	case SPI_NBITS_SINGLE:
@@ -397,11 +395,10 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi,
 }
 
 static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi,
-				      struct spi_flash_read_message *msg,
-				      int hp)
+				      const struct spi_mem_op *op, int hp)
 {
-	int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
-	int addrlen = msg->addr_width;
+	int width = op->data.buswidth ? op->data.buswidth : SPI_NBITS_SINGLE;
+	int addrlen = op->addr.nbytes;
 	u32 data = bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL);
 
 	dev_dbg(&qspi->pdev->dev, "set override mode w %x addrlen %x hp %d\n",
@@ -437,17 +434,17 @@ static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi,
 	/* set the override mode */
 	data |=	BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE;
 	bcm_qspi_write(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL, data);
-	bcm_qspi_bspi_set_xfer_params(qspi, msg->read_opcode, 0, 0, 0);
+	bcm_qspi_bspi_set_xfer_params(qspi, op->cmd.opcode, 0, 0, 0);
 
 	return 0;
 }
 
 static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi,
-				  struct spi_flash_read_message *msg, int hp)
+				  const struct spi_mem_op *op, int hp)
 {
 	int error = 0;
-	int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
-	int addrlen = msg->addr_width;
+	int width = op->data.buswidth ? op->data.buswidth : SPI_NBITS_SINGLE;
+	int addrlen = op->addr.nbytes;
 
 	/* default mode */
 	qspi->xfer_mode.flex_mode = true;
@@ -460,12 +457,12 @@ static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi,
 		if (val & mask || qspi->s3_strap_override_ctrl & mask) {
 			qspi->xfer_mode.flex_mode = false;
 			bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, 0);
-			error = bcm_qspi_bspi_set_override(qspi, msg, hp);
+			error = bcm_qspi_bspi_set_override(qspi, op, hp);
 		}
 	}
 
 	if (qspi->xfer_mode.flex_mode)
-		error = bcm_qspi_bspi_set_flex_mode(qspi, msg, hp);
+		error = bcm_qspi_bspi_set_flex_mode(qspi, op, hp);
 
 	if (error) {
 		dev_warn(&qspi->pdev->dev,
@@ -794,19 +791,24 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi)
 	return slot;
 }
 
-static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
-				    struct spi_flash_read_message *msg)
+static int bcm_qspi_bspi_exec_mem_op(struct spi_device *spi,
+				     const struct spi_mem_op *op)
 {
 	struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
-	u32 addr = 0, len, rdlen, len_words;
-	int ret = 0;
+	u32 addr = 0, len, rdlen, len_words, from = 0;
+	int ret = 0, i;
 	unsigned long timeo = msecs_to_jiffies(100);
 	struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
 
 	if (bcm_qspi_bspi_ver_three(qspi))
-		if (msg->addr_width == BSPI_ADDRLEN_4BYTES)
+		if (op->addr.nbytes == BSPI_ADDRLEN_4BYTES)
 			return -EIO;
 
+	for (i = 0; i < op->addr.nbytes; i++) {
+		from <<= 8;
+		from |= op->addr.buf[i];
+	}
+
 	bcm_qspi_chip_select(qspi, spi->chip_select);
 	bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 0);
 
@@ -815,15 +817,15 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
 	 * the upper address byte to bspi
 	 */
 	if (bcm_qspi_bspi_ver_three(qspi) == false) {
-		addr = msg->from & 0xff000000;
+		addr = from & 0xff000000;
 		bcm_qspi_write(qspi, BSPI,
 			       BSPI_BSPI_FLASH_UPPER_ADDR_BYTE, addr);
 	}
 
 	if (!qspi->xfer_mode.flex_mode)
-		addr = msg->from;
+		addr = from;
 	else
-		addr = msg->from & 0x00ffffff;
+		addr = from & 0x00ffffff;
 
 	if (bcm_qspi_bspi_ver_three(qspi) == true)
 		addr = (addr + 0xc00000) & 0xffffff;
@@ -832,8 +834,8 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
 	 * read into the entire buffer by breaking the reads
 	 * into RAF buffer read lengths
 	 */
-	len = msg->len;
-	qspi->bspi_rf_msg_idx = 0;
+	len = op->data.nbytes;
+	qspi->bspi_rf_op_idx = 0;
 
 	do {
 		if (len > BSPI_READ_LENGTH)
@@ -844,9 +846,9 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
 		reinit_completion(&qspi->bspi_done);
 		bcm_qspi_enable_bspi(qspi);
 		len_words = (rdlen + 3) >> 2;
-		qspi->bspi_rf_msg = msg;
-		qspi->bspi_rf_msg_status = 0;
-		qspi->bspi_rf_msg_len = rdlen;
+		qspi->bspi_rf_op = op;
+		qspi->bspi_rf_op_status = 0;
+		qspi->bspi_rf_op_len = rdlen;
 		dev_dbg(&qspi->pdev->dev,
 			"bspi xfr addr 0x%x len 0x%x", addr, rdlen);
 		bcm_qspi_write(qspi, BSPI, BSPI_RAF_START_ADDR, addr);
@@ -871,7 +873,6 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
 		}
 
 		/* set msg return length */
-		msg->retlen += rdlen;
 		addr += rdlen;
 		len -= rdlen;
 	} while (len);
@@ -906,61 +907,66 @@ static int bcm_qspi_transfer_one(struct spi_master *master,
 	return 0;
 }
 
-static int bcm_qspi_mspi_flash_read(struct spi_device *spi,
-				    struct spi_flash_read_message *msg)
+static int bcm_qspi_mspi_exec_mem_op(struct spi_device *spi,
+				     const struct spi_mem_op *op)
 {
-	struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
+	struct spi_master *master = spi->master;
+	struct bcm_qspi *qspi = spi_master_get_devdata(master);
 	struct spi_transfer t[2];
-	u8 cmd[6];
-	int ret;
+	u8 cmd[6] = { };
+	int ret, i;
 
 	memset(cmd, 0, sizeof(cmd));
 	memset(t, 0, sizeof(t));
 
 	/* tx */
 	/* opcode is in cmd[0] */
-	cmd[0] = msg->read_opcode;
-	cmd[1] = msg->from >> (msg->addr_width * 8 -  8);
-	cmd[2] = msg->from >> (msg->addr_width * 8 - 16);
-	cmd[3] = msg->from >> (msg->addr_width * 8 - 24);
-	cmd[4] = msg->from >> (msg->addr_width * 8 - 32);
+	cmd[0] = op->cmd.opcode;
+	for (i = 0; i < op->addr.nbytes; i++)
+		cmd[1 + i] = op->addr.buf[i];
 	t[0].tx_buf = cmd;
-	t[0].len = msg->addr_width + msg->dummy_bytes + 1;
+	t[0].len = op->addr.nbytes + op->dummy.nbytes + 1;
 	t[0].bits_per_word = spi->bits_per_word;
-	t[0].tx_nbits = msg->opcode_nbits;
+	t[0].tx_nbits = op->cmd.buswidth;
 	/* lets mspi know that this is not last transfer */
 	qspi->trans_pos.mspi_last_trans = false;
-	ret = bcm_qspi_transfer_one(spi->master, spi, &t[0]);
+	ret = bcm_qspi_transfer_one(master, spi, &t[0]);
 
 	/* rx */
 	qspi->trans_pos.mspi_last_trans = true;
 	if (!ret) {
 		/* rx */
-		t[1].rx_buf = msg->buf;
-		t[1].len = msg->len;
-		t[1].rx_nbits =  msg->data_nbits;
+		t[1].rx_buf = op->data.buf.in;
+		t[1].len = op->data.nbytes;
+		t[1].rx_nbits =  op->data.buswidth;
 		t[1].bits_per_word = spi->bits_per_word;
-		ret = bcm_qspi_transfer_one(spi->master, spi, &t[1]);
+		ret = bcm_qspi_transfer_one(master, spi, &t[1]);
 	}
 
-	if (!ret)
-		msg->retlen = msg->len;
-
 	return ret;
 }
 
-static int bcm_qspi_flash_read(struct spi_device *spi,
-			       struct spi_flash_read_message *msg)
+static int bcm_qspi_exec_mem_op(struct spi_device *spi,
+				const struct spi_mem_op *op)
 {
 	struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
-	int ret = 0;
+	int ret = 0, i;
 	bool mspi_read = false;
-	u32 addr, len;
+	u32 addr = 0, len;
 	u_char *buf;
 
-	buf = msg->buf;
-	addr = msg->from;
-	len = msg->len;
+	if (!op->data.nbytes || !op->addr.nbytes || op->addr.nbytes > 4 ||
+	    op->data.dir != SPI_MEM_DATA_IN)
+		return -ENOTSUPP;
+
+	buf = op->data.buf.in;
+
+	for (i = 0; i < op->addr.nbytes; i++) {
+		addr <<= 8;
+		addr |= op->addr.buf[i];
+	}
+
+	len = op->data.nbytes;
 
 	if (bcm_qspi_bspi_ver_three(qspi) == true) {
 		/*
@@ -982,12 +988,44 @@ static int bcm_qspi_flash_read(struct spi_device *spi,
 		mspi_read = true;
 
 	if (mspi_read)
-		return bcm_qspi_mspi_flash_read(spi, msg);
+		return bcm_qspi_mspi_exec_mem_op(spi, op);
 
-	ret = bcm_qspi_bspi_set_mode(qspi, msg, -1);
+	ret = bcm_qspi_bspi_set_mode(qspi, op, -1);
 
 	if (!ret)
-		ret = bcm_qspi_bspi_flash_read(spi, msg);
+		ret = bcm_qspi_bspi_exec_mem_op(spi, op);
+
+	return ret;
+}
+
+static int bcm_qspi_exec_mem_op_wrapper(struct spi_mem *mem,
+					const struct spi_mem_op *op)
+{
+	return bcm_qspi_exec_mem_op(mem->spi, op);
+}
+
+static int bcm_qspi_flash_read_wrapper(struct spi_device *spi,
+				       struct spi_flash_read_message *msg)
+{
+	int i, ret;
+	u8 addrs[4];
+	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(msg->read_opcode, 1),
+					  SPI_MEM_OP_ADDRS(msg->addr_width,
+							   addrs,
+							   msg->addr_nbits),
+					  SPI_MEM_OP_DUMMY(msg->dummy_bytes,
+							   msg->addr_nbits),
+					  SPI_MEM_OP_DATA_IN(msg->len,
+							     msg->buf,
+							     msg->data_nbits));
+
+	for (i = msg->addr_width - 1; i >= 0; i--)
+		addrs[i] = msg->from >> (i * 8);
+
+	msg->retlen = 0;
+	ret = bcm_qspi_exec_mem_op(spi, &op);
+	if (!ret)
+		msg->retlen = msg->len;
 
 	return ret;
 }
@@ -1026,10 +1064,10 @@ static irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id)
 	struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
 	u32 status = qspi_dev_id->irqp->mask;
 
-	if (qspi->bspi_enabled && qspi->bspi_rf_msg) {
+	if (qspi->bspi_enabled && qspi->bspi_rf_op) {
 		bcm_qspi_bspi_lr_data_read(qspi);
-		if (qspi->bspi_rf_msg_len == 0) {
-			qspi->bspi_rf_msg = NULL;
+		if (qspi->bspi_rf_op_len == 0) {
+			qspi->bspi_rf_op = NULL;
 			if (qspi->soc_intc) {
 				/* disable soc BSPI interrupt */
 				soc_intc->bcm_qspi_int_set(soc_intc, BSPI_DONE,
@@ -1038,7 +1076,7 @@ static irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id)
 				status = INTR_BSPI_LR_SESSION_DONE_MASK;
 			}
 
-			if (qspi->bspi_rf_msg_status)
+			if (qspi->bspi_rf_op_status)
 				bcm_qspi_bspi_lr_clear(qspi);
 			else
 				bcm_qspi_bspi_flush_prefetch_buffers(qspi);
@@ -1050,7 +1088,7 @@ static irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id)
 	}
 
 	status &= INTR_BSPI_LR_SESSION_DONE_MASK;
-	if (qspi->bspi_enabled && status && qspi->bspi_rf_msg_len == 0)
+	if (qspi->bspi_enabled && status && qspi->bspi_rf_op_len == 0)
 		complete(&qspi->bspi_done);
 
 	return IRQ_HANDLED;
@@ -1063,7 +1101,7 @@ static irqreturn_t bcm_qspi_bspi_lr_err_l2_isr(int irq, void *dev_id)
 	struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
 
 	dev_err(&qspi->pdev->dev, "BSPI INT error\n");
-	qspi->bspi_rf_msg_status = -EIO;
+	qspi->bspi_rf_op_status = -EIO;
 	if (qspi->soc_intc)
 		/* clear soc interrupt */
 		soc_intc->bcm_qspi_int_ack(soc_intc, BSPI_ERR);
@@ -1186,6 +1224,10 @@ static void bcm_qspi_hw_uninit(struct bcm_qspi *qspi)
 
 }
 
+static const struct spi_controller_mem_ops bcm_qspi_mem_ops = {
+	.exec_op = bcm_qspi_exec_mem_op_wrapper,
+};
+
 static const struct of_device_id bcm_qspi_of_match[] = {
 	{ .compatible = "brcm,spi-bcm-qspi" },
 	{},
@@ -1228,7 +1270,8 @@ int bcm_qspi_probe(struct platform_device *pdev,
 	master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD;
 	master->setup = bcm_qspi_setup;
 	master->transfer_one = bcm_qspi_transfer_one;
-	master->spi_flash_read = bcm_qspi_flash_read;
+	master->spi_flash_read = bcm_qspi_flash_read_wrapper;
+	master->mem_ops = &bcm_qspi_mem_ops;
 	master->cleanup = bcm_qspi_cleanup;
 	master->dev.of_node = dev->of_node;
 	master->num_chipselect = NUM_CHIPSELECT;
-- 
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [RFC PATCH 2/6] spi: bcm-qspi: Implement the spi_mem interface
@ 2018-02-05 23:21     ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

From: Boris Brezillon <boris.brezillon@free-electrons.com>

The spi_mem interface is meant to replace the ->spi_flash_read() one.
Implement the ->exec_op() method to ease removal of the old interface.

Not that ->spi_flash_read() is now implemented as a wrapper around the
new bcm_qspi_exec_mem_op() function so that we can easily get rid of
it when ->spi_flash_read() is removed.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/spi/spi-bcm-qspi.c | 205 +++++++++++++++++++++++++++------------------
 1 file changed, 124 insertions(+), 81 deletions(-)

diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c
index a172ab299e80..f47a13453ef6 100644
--- a/drivers/spi/spi-bcm-qspi.c
+++ b/drivers/spi/spi-bcm-qspi.c
@@ -215,10 +215,10 @@ struct bcm_qspi {
 	int bspi_maj_rev;
 	int bspi_min_rev;
 	int bspi_enabled;
-	struct spi_flash_read_message *bspi_rf_msg;
-	u32 bspi_rf_msg_idx;
-	u32 bspi_rf_msg_len;
-	u32 bspi_rf_msg_status;
+	const struct spi_mem_op *bspi_rf_op;
+	u32 bspi_rf_op_idx;
+	u32 bspi_rf_op_len;
+	u32 bspi_rf_op_status;
 	struct bcm_xfer_mode xfer_mode;
 	u32 s3_strap_override_ctrl;
 	bool bspi_mode;
@@ -313,26 +313,26 @@ static inline void bcm_qspi_bspi_lr_clear(struct bcm_qspi *qspi)
 
 static void bcm_qspi_bspi_lr_data_read(struct bcm_qspi *qspi)
 {
-	u32 *buf = (u32 *)qspi->bspi_rf_msg->buf;
+	u32 *buf = (u32 *)qspi->bspi_rf_op->data.buf.in;
 	u32 data = 0;
 
-	dev_dbg(&qspi->pdev->dev, "xfer %p rx %p rxlen %d\n", qspi->bspi_rf_msg,
-		qspi->bspi_rf_msg->buf, qspi->bspi_rf_msg_len);
+	dev_dbg(&qspi->pdev->dev, "xfer %p rx %p rxlen %d\n", qspi->bspi_rf_op,
+		qspi->bspi_rf_op->data.buf.in, qspi->bspi_rf_op_len);
 	while (!bcm_qspi_bspi_lr_is_fifo_empty(qspi)) {
 		data = bcm_qspi_bspi_lr_read_fifo(qspi);
-		if (likely(qspi->bspi_rf_msg_len >= 4) &&
+		if (likely(qspi->bspi_rf_op_len >= 4) &&
 		    IS_ALIGNED((uintptr_t)buf, 4)) {
-			buf[qspi->bspi_rf_msg_idx++] = data;
-			qspi->bspi_rf_msg_len -= 4;
+			buf[qspi->bspi_rf_op_idx++] = data;
+			qspi->bspi_rf_op_len -= 4;
 		} else {
 			/* Read out remaining bytes, make sure*/
-			u8 *cbuf = (u8 *)&buf[qspi->bspi_rf_msg_idx];
+			u8 *cbuf = (u8 *)&buf[qspi->bspi_rf_op_idx];
 
 			data = cpu_to_le32(data);
-			while (qspi->bspi_rf_msg_len) {
+			while (qspi->bspi_rf_op_len) {
 				*cbuf++ = (u8)data;
 				data >>= 8;
-				qspi->bspi_rf_msg_len--;
+				qspi->bspi_rf_op_len--;
 			}
 		}
 	}
@@ -349,14 +349,12 @@ static void bcm_qspi_bspi_set_xfer_params(struct bcm_qspi *qspi, u8 cmd_byte,
 }
 
 static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi,
-				       struct spi_flash_read_message *msg,
-				       int hp)
+				       const struct spi_mem_op *op, int hp)
 {
 	int bpc = 0, bpp = 0;
-	u8 command = msg->read_opcode;
-	int width  = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
-	int addrlen = msg->addr_width;
-	int addr_nbits = msg->addr_nbits ? msg->addr_nbits : SPI_NBITS_SINGLE;
+	u8 command = op->cmd.opcode;
+	int width  = op->cmd.buswidth ? op->cmd.buswidth : SPI_NBITS_SINGLE;
+	int addrlen = op->addr.nbytes * 8;
 	int flex_mode = 1;
 
 	dev_dbg(&qspi->pdev->dev, "set flex mode w %x addrlen %x hp %d\n",
@@ -365,7 +363,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi,
 	if (addrlen == BSPI_ADDRLEN_4BYTES)
 		bpp = BSPI_BPP_ADDR_SELECT_MASK;
 
-	bpp |= msg->dummy_bytes * (8/addr_nbits);
+	bpp |= (op->dummy.nbytes * 8) / op->dummy.buswidth;
 
 	switch (width) {
 	case SPI_NBITS_SINGLE:
@@ -397,11 +395,10 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi,
 }
 
 static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi,
-				      struct spi_flash_read_message *msg,
-				      int hp)
+				      const struct spi_mem_op *op, int hp)
 {
-	int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
-	int addrlen = msg->addr_width;
+	int width = op->data.buswidth ? op->data.buswidth : SPI_NBITS_SINGLE;
+	int addrlen = op->addr.nbytes;
 	u32 data = bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL);
 
 	dev_dbg(&qspi->pdev->dev, "set override mode w %x addrlen %x hp %d\n",
@@ -437,17 +434,17 @@ static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi,
 	/* set the override mode */
 	data |=	BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE;
 	bcm_qspi_write(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL, data);
-	bcm_qspi_bspi_set_xfer_params(qspi, msg->read_opcode, 0, 0, 0);
+	bcm_qspi_bspi_set_xfer_params(qspi, op->cmd.opcode, 0, 0, 0);
 
 	return 0;
 }
 
 static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi,
-				  struct spi_flash_read_message *msg, int hp)
+				  const struct spi_mem_op *op, int hp)
 {
 	int error = 0;
-	int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
-	int addrlen = msg->addr_width;
+	int width = op->data.buswidth ? op->data.buswidth : SPI_NBITS_SINGLE;
+	int addrlen = op->addr.nbytes;
 
 	/* default mode */
 	qspi->xfer_mode.flex_mode = true;
@@ -460,12 +457,12 @@ static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi,
 		if (val & mask || qspi->s3_strap_override_ctrl & mask) {
 			qspi->xfer_mode.flex_mode = false;
 			bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, 0);
-			error = bcm_qspi_bspi_set_override(qspi, msg, hp);
+			error = bcm_qspi_bspi_set_override(qspi, op, hp);
 		}
 	}
 
 	if (qspi->xfer_mode.flex_mode)
-		error = bcm_qspi_bspi_set_flex_mode(qspi, msg, hp);
+		error = bcm_qspi_bspi_set_flex_mode(qspi, op, hp);
 
 	if (error) {
 		dev_warn(&qspi->pdev->dev,
@@ -794,19 +791,24 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi)
 	return slot;
 }
 
-static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
-				    struct spi_flash_read_message *msg)
+static int bcm_qspi_bspi_exec_mem_op(struct spi_device *spi,
+				     const struct spi_mem_op *op)
 {
 	struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
-	u32 addr = 0, len, rdlen, len_words;
-	int ret = 0;
+	u32 addr = 0, len, rdlen, len_words, from = 0;
+	int ret = 0, i;
 	unsigned long timeo = msecs_to_jiffies(100);
 	struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
 
 	if (bcm_qspi_bspi_ver_three(qspi))
-		if (msg->addr_width == BSPI_ADDRLEN_4BYTES)
+		if (op->addr.nbytes == BSPI_ADDRLEN_4BYTES)
 			return -EIO;
 
+	for (i = 0; i < op->addr.nbytes; i++) {
+		from <<= 8;
+		from |= op->addr.buf[i];
+	}
+
 	bcm_qspi_chip_select(qspi, spi->chip_select);
 	bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 0);
 
@@ -815,15 +817,15 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
 	 * the upper address byte to bspi
 	 */
 	if (bcm_qspi_bspi_ver_three(qspi) == false) {
-		addr = msg->from & 0xff000000;
+		addr = from & 0xff000000;
 		bcm_qspi_write(qspi, BSPI,
 			       BSPI_BSPI_FLASH_UPPER_ADDR_BYTE, addr);
 	}
 
 	if (!qspi->xfer_mode.flex_mode)
-		addr = msg->from;
+		addr = from;
 	else
-		addr = msg->from & 0x00ffffff;
+		addr = from & 0x00ffffff;
 
 	if (bcm_qspi_bspi_ver_three(qspi) == true)
 		addr = (addr + 0xc00000) & 0xffffff;
@@ -832,8 +834,8 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
 	 * read into the entire buffer by breaking the reads
 	 * into RAF buffer read lengths
 	 */
-	len = msg->len;
-	qspi->bspi_rf_msg_idx = 0;
+	len = op->data.nbytes;
+	qspi->bspi_rf_op_idx = 0;
 
 	do {
 		if (len > BSPI_READ_LENGTH)
@@ -844,9 +846,9 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
 		reinit_completion(&qspi->bspi_done);
 		bcm_qspi_enable_bspi(qspi);
 		len_words = (rdlen + 3) >> 2;
-		qspi->bspi_rf_msg = msg;
-		qspi->bspi_rf_msg_status = 0;
-		qspi->bspi_rf_msg_len = rdlen;
+		qspi->bspi_rf_op = op;
+		qspi->bspi_rf_op_status = 0;
+		qspi->bspi_rf_op_len = rdlen;
 		dev_dbg(&qspi->pdev->dev,
 			"bspi xfr addr 0x%x len 0x%x", addr, rdlen);
 		bcm_qspi_write(qspi, BSPI, BSPI_RAF_START_ADDR, addr);
@@ -871,7 +873,6 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
 		}
 
 		/* set msg return length */
-		msg->retlen += rdlen;
 		addr += rdlen;
 		len -= rdlen;
 	} while (len);
@@ -906,61 +907,66 @@ static int bcm_qspi_transfer_one(struct spi_master *master,
 	return 0;
 }
 
-static int bcm_qspi_mspi_flash_read(struct spi_device *spi,
-				    struct spi_flash_read_message *msg)
+static int bcm_qspi_mspi_exec_mem_op(struct spi_device *spi,
+				     const struct spi_mem_op *op)
 {
-	struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
+	struct spi_master *master = spi->master;
+	struct bcm_qspi *qspi = spi_master_get_devdata(master);
 	struct spi_transfer t[2];
-	u8 cmd[6];
-	int ret;
+	u8 cmd[6] = { };
+	int ret, i;
 
 	memset(cmd, 0, sizeof(cmd));
 	memset(t, 0, sizeof(t));
 
 	/* tx */
 	/* opcode is in cmd[0] */
-	cmd[0] = msg->read_opcode;
-	cmd[1] = msg->from >> (msg->addr_width * 8 -  8);
-	cmd[2] = msg->from >> (msg->addr_width * 8 - 16);
-	cmd[3] = msg->from >> (msg->addr_width * 8 - 24);
-	cmd[4] = msg->from >> (msg->addr_width * 8 - 32);
+	cmd[0] = op->cmd.opcode;
+	for (i = 0; i < op->addr.nbytes; i++)
+		cmd[1 + i] = op->addr.buf[i];
 	t[0].tx_buf = cmd;
-	t[0].len = msg->addr_width + msg->dummy_bytes + 1;
+	t[0].len = op->addr.nbytes + op->dummy.nbytes + 1;
 	t[0].bits_per_word = spi->bits_per_word;
-	t[0].tx_nbits = msg->opcode_nbits;
+	t[0].tx_nbits = op->cmd.buswidth;
 	/* lets mspi know that this is not last transfer */
 	qspi->trans_pos.mspi_last_trans = false;
-	ret = bcm_qspi_transfer_one(spi->master, spi, &t[0]);
+	ret = bcm_qspi_transfer_one(master, spi, &t[0]);
 
 	/* rx */
 	qspi->trans_pos.mspi_last_trans = true;
 	if (!ret) {
 		/* rx */
-		t[1].rx_buf = msg->buf;
-		t[1].len = msg->len;
-		t[1].rx_nbits =  msg->data_nbits;
+		t[1].rx_buf = op->data.buf.in;
+		t[1].len = op->data.nbytes;
+		t[1].rx_nbits =  op->data.buswidth;
 		t[1].bits_per_word = spi->bits_per_word;
-		ret = bcm_qspi_transfer_one(spi->master, spi, &t[1]);
+		ret = bcm_qspi_transfer_one(master, spi, &t[1]);
 	}
 
-	if (!ret)
-		msg->retlen = msg->len;
-
 	return ret;
 }
 
-static int bcm_qspi_flash_read(struct spi_device *spi,
-			       struct spi_flash_read_message *msg)
+static int bcm_qspi_exec_mem_op(struct spi_device *spi,
+				const struct spi_mem_op *op)
 {
 	struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
-	int ret = 0;
+	int ret = 0, i;
 	bool mspi_read = false;
-	u32 addr, len;
+	u32 addr = 0, len;
 	u_char *buf;
 
-	buf = msg->buf;
-	addr = msg->from;
-	len = msg->len;
+	if (!op->data.nbytes || !op->addr.nbytes || op->addr.nbytes > 4 ||
+	    op->data.dir != SPI_MEM_DATA_IN)
+		return -ENOTSUPP;
+
+	buf = op->data.buf.in;
+
+	for (i = 0; i < op->addr.nbytes; i++) {
+		addr <<= 8;
+		addr |= op->addr.buf[i];
+	}
+
+	len = op->data.nbytes;
 
 	if (bcm_qspi_bspi_ver_three(qspi) == true) {
 		/*
@@ -982,12 +988,44 @@ static int bcm_qspi_flash_read(struct spi_device *spi,
 		mspi_read = true;
 
 	if (mspi_read)
-		return bcm_qspi_mspi_flash_read(spi, msg);
+		return bcm_qspi_mspi_exec_mem_op(spi, op);
 
-	ret = bcm_qspi_bspi_set_mode(qspi, msg, -1);
+	ret = bcm_qspi_bspi_set_mode(qspi, op, -1);
 
 	if (!ret)
-		ret = bcm_qspi_bspi_flash_read(spi, msg);
+		ret = bcm_qspi_bspi_exec_mem_op(spi, op);
+
+	return ret;
+}
+
+static int bcm_qspi_exec_mem_op_wrapper(struct spi_mem *mem,
+					const struct spi_mem_op *op)
+{
+	return bcm_qspi_exec_mem_op(mem->spi, op);
+}
+
+static int bcm_qspi_flash_read_wrapper(struct spi_device *spi,
+				       struct spi_flash_read_message *msg)
+{
+	int i, ret;
+	u8 addrs[4];
+	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(msg->read_opcode, 1),
+					  SPI_MEM_OP_ADDRS(msg->addr_width,
+							   addrs,
+							   msg->addr_nbits),
+					  SPI_MEM_OP_DUMMY(msg->dummy_bytes,
+							   msg->addr_nbits),
+					  SPI_MEM_OP_DATA_IN(msg->len,
+							     msg->buf,
+							     msg->data_nbits));
+
+	for (i = msg->addr_width - 1; i >= 0; i--)
+		addrs[i] = msg->from >> (i * 8);
+
+	msg->retlen = 0;
+	ret = bcm_qspi_exec_mem_op(spi, &op);
+	if (!ret)
+		msg->retlen = msg->len;
 
 	return ret;
 }
@@ -1026,10 +1064,10 @@ static irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id)
 	struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
 	u32 status = qspi_dev_id->irqp->mask;
 
-	if (qspi->bspi_enabled && qspi->bspi_rf_msg) {
+	if (qspi->bspi_enabled && qspi->bspi_rf_op) {
 		bcm_qspi_bspi_lr_data_read(qspi);
-		if (qspi->bspi_rf_msg_len == 0) {
-			qspi->bspi_rf_msg = NULL;
+		if (qspi->bspi_rf_op_len == 0) {
+			qspi->bspi_rf_op = NULL;
 			if (qspi->soc_intc) {
 				/* disable soc BSPI interrupt */
 				soc_intc->bcm_qspi_int_set(soc_intc, BSPI_DONE,
@@ -1038,7 +1076,7 @@ static irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id)
 				status = INTR_BSPI_LR_SESSION_DONE_MASK;
 			}
 
-			if (qspi->bspi_rf_msg_status)
+			if (qspi->bspi_rf_op_status)
 				bcm_qspi_bspi_lr_clear(qspi);
 			else
 				bcm_qspi_bspi_flush_prefetch_buffers(qspi);
@@ -1050,7 +1088,7 @@ static irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id)
 	}
 
 	status &= INTR_BSPI_LR_SESSION_DONE_MASK;
-	if (qspi->bspi_enabled && status && qspi->bspi_rf_msg_len == 0)
+	if (qspi->bspi_enabled && status && qspi->bspi_rf_op_len == 0)
 		complete(&qspi->bspi_done);
 
 	return IRQ_HANDLED;
@@ -1063,7 +1101,7 @@ static irqreturn_t bcm_qspi_bspi_lr_err_l2_isr(int irq, void *dev_id)
 	struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
 
 	dev_err(&qspi->pdev->dev, "BSPI INT error\n");
-	qspi->bspi_rf_msg_status = -EIO;
+	qspi->bspi_rf_op_status = -EIO;
 	if (qspi->soc_intc)
 		/* clear soc interrupt */
 		soc_intc->bcm_qspi_int_ack(soc_intc, BSPI_ERR);
@@ -1186,6 +1224,10 @@ static void bcm_qspi_hw_uninit(struct bcm_qspi *qspi)
 
 }
 
+static const struct spi_controller_mem_ops bcm_qspi_mem_ops = {
+	.exec_op = bcm_qspi_exec_mem_op_wrapper,
+};
+
 static const struct of_device_id bcm_qspi_of_match[] = {
 	{ .compatible = "brcm,spi-bcm-qspi" },
 	{},
@@ -1228,7 +1270,8 @@ int bcm_qspi_probe(struct platform_device *pdev,
 	master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD;
 	master->setup = bcm_qspi_setup;
 	master->transfer_one = bcm_qspi_transfer_one;
-	master->spi_flash_read = bcm_qspi_flash_read;
+	master->spi_flash_read = bcm_qspi_flash_read_wrapper;
+	master->mem_ops = &bcm_qspi_mem_ops;
 	master->cleanup = bcm_qspi_cleanup;
 	master->dev.of_node = dev->of_node;
 	master->num_chipselect = NUM_CHIPSELECT;
-- 
2.14.1

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

* [RFC PATCH 3/6] spi: bcm53xx: Implement the spi_mem interface
  2018-02-05 23:21 ` Boris Brezillon
@ 2018-02-05 23:21     ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

The spi_mem interface is meant to replace the spi_flash_read() one.
Implement the ->exec_op() method so that we can smoothly get rid of the
old interface.

Note that the current ->flash_read() implementation looks a bit fragile
since it does not take the ->read_opcode passed by the spi-nor layer
into account, which means if might not work with all kind of NORs.

Anyway, I left the logic unchanged and added a few extra checks to make
sure we're receiving something that looks like a NOR read operation.

Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/spi/spi-bcm53xx.c | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-bcm53xx.c b/drivers/spi/spi-bcm53xx.c
index 6e409eabe1c9..c681d432e26c 100644
--- a/drivers/spi/spi-bcm53xx.c
+++ b/drivers/spi/spi-bcm53xx.c
@@ -263,6 +263,40 @@ static int bcm53xxspi_transfer_one(struct spi_master *master,
 	return 0;
 }
 
+static int bcm53xxspi_exec_mem_op(struct spi_mem *mem,
+				  const struct spi_mem_op *op)
+{
+	struct bcm53xxspi *b53spi = spi_master_get_devdata(mem->spi->master);
+	u32 from;
+
+	/*
+	 * FIXME: There's nothing in this driver programming the opcode and
+	 * buswidth to be used when a read is done on the mmio window, but it
+	 * seems to be used to access a SPI NOR device, so restrict access
+	 * access to SPINOR_OP_READ commands.
+	 */
+	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
+	    op->addr.nbytes != 3 || op->cmd.opcode != 0x3)
+		return -ENOTSUPP;
+
+	from = ((u32)op->addr.buf[0] << 16) | ((u32)op->addr.buf[1] << 8) |
+	       op->addr.buf[2];
+
+	/* Return -ENOTSUPP so that the core can fall back to normal reads. */
+	if (from + op->data.nbytes > BCM53XXSPI_FLASH_WINDOW)
+		return -ENOTSUPP;
+
+	bcm53xxspi_enable_bspi(b53spi);
+	memcpy_fromio(op->data.buf.in, b53spi->mmio_base + from,
+		      op->data.nbytes);
+
+	return 0;
+}
+
+static const struct spi_controller_mem_ops bcm53xxspi_mem_ops = {
+	.exec_op = bcm53xxspi_exec_mem_op,
+};
+
 static int bcm53xxspi_flash_read(struct spi_device *spi,
 				 struct spi_flash_read_message *msg)
 {
@@ -317,8 +351,10 @@ static int bcm53xxspi_bcma_probe(struct bcma_device *core)
 
 	master->dev.of_node = dev->of_node;
 	master->transfer_one = bcm53xxspi_transfer_one;
-	if (b53spi->mmio_base)
+	if (b53spi->mmio_base) {
+		master->mem_ops = &bcm53xxspi_mem_ops;
 		master->spi_flash_read = bcm53xxspi_flash_read;
+	}
 
 	bcma_set_drvdata(core, b53spi);
 
-- 
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [RFC PATCH 3/6] spi: bcm53xx: Implement the spi_mem interface
@ 2018-02-05 23:21     ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

From: Boris Brezillon <boris.brezillon@free-electrons.com>

The spi_mem interface is meant to replace the spi_flash_read() one.
Implement the ->exec_op() method so that we can smoothly get rid of the
old interface.

Note that the current ->flash_read() implementation looks a bit fragile
since it does not take the ->read_opcode passed by the spi-nor layer
into account, which means if might not work with all kind of NORs.

Anyway, I left the logic unchanged and added a few extra checks to make
sure we're receiving something that looks like a NOR read operation.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/spi/spi-bcm53xx.c | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-bcm53xx.c b/drivers/spi/spi-bcm53xx.c
index 6e409eabe1c9..c681d432e26c 100644
--- a/drivers/spi/spi-bcm53xx.c
+++ b/drivers/spi/spi-bcm53xx.c
@@ -263,6 +263,40 @@ static int bcm53xxspi_transfer_one(struct spi_master *master,
 	return 0;
 }
 
+static int bcm53xxspi_exec_mem_op(struct spi_mem *mem,
+				  const struct spi_mem_op *op)
+{
+	struct bcm53xxspi *b53spi = spi_master_get_devdata(mem->spi->master);
+	u32 from;
+
+	/*
+	 * FIXME: There's nothing in this driver programming the opcode and
+	 * buswidth to be used when a read is done on the mmio window, but it
+	 * seems to be used to access a SPI NOR device, so restrict access
+	 * access to SPINOR_OP_READ commands.
+	 */
+	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
+	    op->addr.nbytes != 3 || op->cmd.opcode != 0x3)
+		return -ENOTSUPP;
+
+	from = ((u32)op->addr.buf[0] << 16) | ((u32)op->addr.buf[1] << 8) |
+	       op->addr.buf[2];
+
+	/* Return -ENOTSUPP so that the core can fall back to normal reads. */
+	if (from + op->data.nbytes > BCM53XXSPI_FLASH_WINDOW)
+		return -ENOTSUPP;
+
+	bcm53xxspi_enable_bspi(b53spi);
+	memcpy_fromio(op->data.buf.in, b53spi->mmio_base + from,
+		      op->data.nbytes);
+
+	return 0;
+}
+
+static const struct spi_controller_mem_ops bcm53xxspi_mem_ops = {
+	.exec_op = bcm53xxspi_exec_mem_op,
+};
+
 static int bcm53xxspi_flash_read(struct spi_device *spi,
 				 struct spi_flash_read_message *msg)
 {
@@ -317,8 +351,10 @@ static int bcm53xxspi_bcma_probe(struct bcma_device *core)
 
 	master->dev.of_node = dev->of_node;
 	master->transfer_one = bcm53xxspi_transfer_one;
-	if (b53spi->mmio_base)
+	if (b53spi->mmio_base) {
+		master->mem_ops = &bcm53xxspi_mem_ops;
 		master->spi_flash_read = bcm53xxspi_flash_read;
+	}
 
 	bcma_set_drvdata(core, b53spi);
 
-- 
2.14.1

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

* [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-05 23:21 ` Boris Brezillon
@ 2018-02-05 23:21     ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

The spi_mem interface is meant to replace the spi_flash_read() one.
Implement the ->exec_op() method so that we can smoothly get rid of the
old interface.

Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 72 insertions(+), 13 deletions(-)

diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
index c24d9b45a27c..40cac3ef6cc9 100644
--- a/drivers/spi/spi-ti-qspi.c
+++ b/drivers/spi/spi-ti-qspi.c
@@ -434,12 +434,10 @@ static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst,
 	return 0;
 }
 
-static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi,
-				     struct spi_flash_read_message *msg)
+static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi, loff_t offs,
+				     void *to, size_t readsize)
 {
-	size_t readsize = msg->len;
-	void *to = msg->buf;
-	dma_addr_t dma_src = qspi->mmap_phys_base + msg->from;
+	dma_addr_t dma_src = qspi->mmap_phys_base + offs;
 	int ret = 0;
 
 	/*
@@ -507,13 +505,14 @@ static void ti_qspi_disable_memory_map(struct spi_device *spi)
 	qspi->mmap_enabled = false;
 }
 
-static void ti_qspi_setup_mmap_read(struct spi_device *spi,
-				    struct spi_flash_read_message *msg)
+static void ti_qspi_setup_mmap_read(struct spi_device *spi, u8 opcode,
+				    u8 data_nbits, u8 addr_width,
+				    u8 dummy_bytes)
 {
 	struct ti_qspi  *qspi = spi_master_get_devdata(spi->master);
-	u32 memval = msg->read_opcode;
+	u32 memval = opcode;
 
-	switch (msg->data_nbits) {
+	switch (data_nbits) {
 	case SPI_NBITS_QUAD:
 		memval |= QSPI_SETUP_RD_QUAD;
 		break;
@@ -524,8 +523,8 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi,
 		memval |= QSPI_SETUP_RD_NORMAL;
 		break;
 	}
-	memval |= ((msg->addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
-		   msg->dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
+	memval |= ((addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
+		   dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
 	ti_qspi_write(qspi, memval,
 		      QSPI_SPI_SETUP_REG(spi->chip_select));
 }
@@ -546,13 +545,15 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
 
 	if (!qspi->mmap_enabled)
 		ti_qspi_enable_memory_map(spi);
-	ti_qspi_setup_mmap_read(spi, msg);
+	ti_qspi_setup_mmap_read(spi, msg->read_opcode, msg->data_nbits,
+				msg->addr_width, msg->dummy_bytes);
 
 	if (qspi->rx_chan) {
 		if (msg->cur_msg_mapped)
 			ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from);
 		else
-			ret = ti_qspi_dma_bounce_buffer(qspi, msg);
+			ret = ti_qspi_dma_bounce_buffer(qspi, msg->from,
+							msg->buf, msg->len);
 		if (ret)
 			goto err_unlock;
 	} else {
@@ -566,6 +567,62 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
 	return ret;
 }
 
+static int ti_qspi_exec_mem_op(struct spi_mem *mem,
+			       const struct spi_mem_op *op)
+{
+	struct ti_qspi *qspi = spi_master_get_devdata(mem->spi->master);
+	int i, ret = 0;
+	u32 from = 0;
+
+	/* Only optimize read path. */
+	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
+	    !op->addr.nbytes || op->addr.nbytes > 4)
+		return -ENOTSUPP;
+
+	for (i = 0; i < op->addr.nbytes; i++) {
+		from <<= 8;
+		from |= op->addr.buf[i];
+	}
+
+	/* Address exceeds MMIO window size, fall back to regular mode. */
+	if (from > 0x4000000)
+		return -ENOTSUPP;
+
+	mutex_lock(&qspi->list_lock);
+
+	if (!qspi->mmap_enabled)
+		ti_qspi_enable_memory_map(mem->spi);
+	ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
+				op->addr.nbytes, op->dummy.nbytes);
+
+	if (qspi->rx_chan) {
+		struct sg_table sgt;
+
+		if (!virt_addr_valid(op->data.buf.in) &&
+		    !spi_controller_dma_map_mem_op_data(mem->spi->master, op,
+							&sgt)) {
+			ret = ti_qspi_dma_xfer_sg(qspi, sgt, from);
+			spi_controller_dma_unmap_mem_op_data(mem->spi->master,
+							     op, &sgt);
+		} else {
+			ret = ti_qspi_dma_bounce_buffer(qspi, from,
+							op->data.buf.in,
+							op->data.nbytes);
+		}
+	} else {
+		memcpy_fromio(op->data.buf.in, qspi->mmap_base + from,
+			      op->data.nbytes);
+	}
+
+	mutex_unlock(&qspi->list_lock);
+
+	return ret;
+}
+
+static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
+	.exec_op = ti_qspi_exec_mem_op,
+};
+
 static int ti_qspi_start_transfer_one(struct spi_master *master,
 		struct spi_message *m)
 {
@@ -673,6 +730,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
 	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
 				     SPI_BPW_MASK(8);
 	master->spi_flash_read = ti_qspi_spi_flash_read;
+	master->mem_ops = &ti_qspi_mem_ops;
 
 	if (!of_property_read_u32(np, "num-cs", &num_cs))
 		master->num_chipselect = num_cs;
@@ -785,6 +843,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
 				 PTR_ERR(qspi->mmap_base));
 			qspi->mmap_base = NULL;
 			master->spi_flash_read = NULL;
+			master->mem_ops = NULL;
 		}
 	}
 	qspi->mmap_enabled = false;
-- 
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-05 23:21     ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

From: Boris Brezillon <boris.brezillon@free-electrons.com>

The spi_mem interface is meant to replace the spi_flash_read() one.
Implement the ->exec_op() method so that we can smoothly get rid of the
old interface.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 72 insertions(+), 13 deletions(-)

diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
index c24d9b45a27c..40cac3ef6cc9 100644
--- a/drivers/spi/spi-ti-qspi.c
+++ b/drivers/spi/spi-ti-qspi.c
@@ -434,12 +434,10 @@ static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst,
 	return 0;
 }
 
-static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi,
-				     struct spi_flash_read_message *msg)
+static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi, loff_t offs,
+				     void *to, size_t readsize)
 {
-	size_t readsize = msg->len;
-	void *to = msg->buf;
-	dma_addr_t dma_src = qspi->mmap_phys_base + msg->from;
+	dma_addr_t dma_src = qspi->mmap_phys_base + offs;
 	int ret = 0;
 
 	/*
@@ -507,13 +505,14 @@ static void ti_qspi_disable_memory_map(struct spi_device *spi)
 	qspi->mmap_enabled = false;
 }
 
-static void ti_qspi_setup_mmap_read(struct spi_device *spi,
-				    struct spi_flash_read_message *msg)
+static void ti_qspi_setup_mmap_read(struct spi_device *spi, u8 opcode,
+				    u8 data_nbits, u8 addr_width,
+				    u8 dummy_bytes)
 {
 	struct ti_qspi  *qspi = spi_master_get_devdata(spi->master);
-	u32 memval = msg->read_opcode;
+	u32 memval = opcode;
 
-	switch (msg->data_nbits) {
+	switch (data_nbits) {
 	case SPI_NBITS_QUAD:
 		memval |= QSPI_SETUP_RD_QUAD;
 		break;
@@ -524,8 +523,8 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi,
 		memval |= QSPI_SETUP_RD_NORMAL;
 		break;
 	}
-	memval |= ((msg->addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
-		   msg->dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
+	memval |= ((addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
+		   dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
 	ti_qspi_write(qspi, memval,
 		      QSPI_SPI_SETUP_REG(spi->chip_select));
 }
@@ -546,13 +545,15 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
 
 	if (!qspi->mmap_enabled)
 		ti_qspi_enable_memory_map(spi);
-	ti_qspi_setup_mmap_read(spi, msg);
+	ti_qspi_setup_mmap_read(spi, msg->read_opcode, msg->data_nbits,
+				msg->addr_width, msg->dummy_bytes);
 
 	if (qspi->rx_chan) {
 		if (msg->cur_msg_mapped)
 			ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from);
 		else
-			ret = ti_qspi_dma_bounce_buffer(qspi, msg);
+			ret = ti_qspi_dma_bounce_buffer(qspi, msg->from,
+							msg->buf, msg->len);
 		if (ret)
 			goto err_unlock;
 	} else {
@@ -566,6 +567,62 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
 	return ret;
 }
 
+static int ti_qspi_exec_mem_op(struct spi_mem *mem,
+			       const struct spi_mem_op *op)
+{
+	struct ti_qspi *qspi = spi_master_get_devdata(mem->spi->master);
+	int i, ret = 0;
+	u32 from = 0;
+
+	/* Only optimize read path. */
+	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
+	    !op->addr.nbytes || op->addr.nbytes > 4)
+		return -ENOTSUPP;
+
+	for (i = 0; i < op->addr.nbytes; i++) {
+		from <<= 8;
+		from |= op->addr.buf[i];
+	}
+
+	/* Address exceeds MMIO window size, fall back to regular mode. */
+	if (from > 0x4000000)
+		return -ENOTSUPP;
+
+	mutex_lock(&qspi->list_lock);
+
+	if (!qspi->mmap_enabled)
+		ti_qspi_enable_memory_map(mem->spi);
+	ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
+				op->addr.nbytes, op->dummy.nbytes);
+
+	if (qspi->rx_chan) {
+		struct sg_table sgt;
+
+		if (!virt_addr_valid(op->data.buf.in) &&
+		    !spi_controller_dma_map_mem_op_data(mem->spi->master, op,
+							&sgt)) {
+			ret = ti_qspi_dma_xfer_sg(qspi, sgt, from);
+			spi_controller_dma_unmap_mem_op_data(mem->spi->master,
+							     op, &sgt);
+		} else {
+			ret = ti_qspi_dma_bounce_buffer(qspi, from,
+							op->data.buf.in,
+							op->data.nbytes);
+		}
+	} else {
+		memcpy_fromio(op->data.buf.in, qspi->mmap_base + from,
+			      op->data.nbytes);
+	}
+
+	mutex_unlock(&qspi->list_lock);
+
+	return ret;
+}
+
+static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
+	.exec_op = ti_qspi_exec_mem_op,
+};
+
 static int ti_qspi_start_transfer_one(struct spi_master *master,
 		struct spi_message *m)
 {
@@ -673,6 +730,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
 	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
 				     SPI_BPW_MASK(8);
 	master->spi_flash_read = ti_qspi_spi_flash_read;
+	master->mem_ops = &ti_qspi_mem_ops;
 
 	if (!of_property_read_u32(np, "num-cs", &num_cs))
 		master->num_chipselect = num_cs;
@@ -785,6 +843,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
 				 PTR_ERR(qspi->mmap_base));
 			qspi->mmap_base = NULL;
 			master->spi_flash_read = NULL;
+			master->mem_ops = NULL;
 		}
 	}
 	qspi->mmap_enabled = false;
-- 
2.14.1

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

* [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API
  2018-02-05 23:21 ` Boris Brezillon
@ 2018-02-05 23:21     ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

The spi_mem_xxx() API has been introduced to replace the
spi_flash_read() one. Make use of it so we can get rid of
spi_flash_read().

Note that using spi_mem_xx() also simplifies the code because this API
takes care of using the regular spi_sync() interface when the optimized
->mem_ops interface is not implemented by the controller.

Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/mtd/devices/m25p80.c | 240 ++++++++++++++++---------------------------
 1 file changed, 88 insertions(+), 152 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index a4e18f6aaa33..3b17425d5e6f 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -29,7 +29,7 @@
 
 #define	MAX_CMD_SIZE		6
 struct m25p {
-	struct spi_device	*spi;
+	struct spi_mem		*spimem;
 	struct spi_nor		spi_nor;
 	u8			command[MAX_CMD_SIZE];
 };
@@ -37,97 +37,80 @@ struct m25p {
 static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
+	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
+					  SPI_MEM_OP_NO_ADDRS,
+					  SPI_MEM_OP_NO_DUMMY,
+					  SPI_MEM_OP_DATA_IN(len, val, 1));
 	int ret;
 
-	ret = spi_write_then_read(spi, &code, 1, val, len);
+	ret = spi_mem_exec_op(flash->spimem, &op);
 	if (ret < 0)
-		dev_err(&spi->dev, "error %d reading %x\n", ret, code);
+		dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret,
+			code);
 
 	return ret;
 }
 
-static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
+static void m25p_offs2addr(struct spi_nor *nor, unsigned int offs, u8 *addrs)
 {
-	/* opcode is in cmd[0] */
-	cmd[1] = addr >> (nor->addr_width * 8 -  8);
-	cmd[2] = addr >> (nor->addr_width * 8 - 16);
-	cmd[3] = addr >> (nor->addr_width * 8 - 24);
-	cmd[4] = addr >> (nor->addr_width * 8 - 32);
-}
-
-static int m25p_cmdsz(struct spi_nor *nor)
-{
-	return 1 + nor->addr_width;
+	addrs[0] = offs >> (nor->addr_width * 8 -  8);
+	addrs[1] = offs >> (nor->addr_width * 8 - 16);
+	addrs[2] = offs >> (nor->addr_width * 8 - 24);
+	addrs[3] = offs >> (nor->addr_width * 8 - 32);
 }
 
 static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
+	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
+					  SPI_MEM_OP_NO_ADDRS,
+					  SPI_MEM_OP_NO_DUMMY,
+					  SPI_MEM_OP_DATA_OUT(len, buf, 1));
 
-	flash->command[0] = opcode;
-	if (buf)
-		memcpy(&flash->command[1], buf, len);
-
-	return spi_write(spi, flash->command, len + 1);
+	return spi_mem_exec_op(flash->spimem, &op);
 }
 
 static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
 			    const u_char *buf)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
-	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
-	struct spi_transfer t[3] = {};
-	struct spi_message m;
-	int cmd_sz = m25p_cmdsz(nor);
-	ssize_t ret;
+	u8 addrs[4];
+	struct spi_mem_op op =
+			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
+				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
+				   SPI_MEM_OP_DUMMY(0, 1),
+				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
+	size_t remaining = len;
+	int ret;
 
 	/* get transfer protocols. */
-	inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto);
-	addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto);
-	data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto);
-
-	spi_message_init(&m);
+	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)
-		cmd_sz = 1;
-
-	flash->command[0] = nor->program_opcode;
-	m25p_addr2cmd(nor, to, flash->command);
+		op.addr.nbytes = 0;
 
-	t[0].tx_buf = flash->command;
-	t[0].tx_nbits = inst_nbits;
-	t[0].len = cmd_sz;
-	spi_message_add_tail(&t[0], &m);
+	while (remaining) {
+		if (op.addr.nbytes)
+			m25p_offs2addr(nor, to, addrs);
 
-	/* split the op code and address bytes into two transfers if needed. */
-	data_idx = 1;
-	if (addr_nbits != inst_nbits) {
-		t[0].len = 1;
+		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+		ret = spi_mem_adjust_op_size(flash->spimem, &op);
+		if (ret)
+			return ret;
 
-		t[1].tx_buf = &flash->command[1];
-		t[1].tx_nbits = addr_nbits;
-		t[1].len = cmd_sz - 1;
-		spi_message_add_tail(&t[1], &m);
+		ret = spi_mem_exec_op(flash->spimem, &op);
+		if (ret)
+			return ret;
 
-		data_idx = 2;
+		to += op.data.nbytes;
+		remaining -= op.data.nbytes;
+		op.data.buf.out += op.data.nbytes;
 	}
 
-	t[data_idx].tx_buf = buf;
-	t[data_idx].tx_nbits = data_nbits;
-	t[data_idx].len = len;
-	spi_message_add_tail(&t[data_idx], &m);
-
-	ret = spi_sync(spi, &m);
-	if (ret)
-		return ret;
-
-	ret = m.actual_length - cmd_sz;
-	if (ret < 0)
-		return -EIO;
-	return ret;
+	return len;
 }
 
 /*
@@ -138,92 +121,42 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
 			   u_char *buf)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
-	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
-	struct spi_transfer t[3];
-	struct spi_message m;
-	unsigned int dummy = nor->read_dummy;
-	ssize_t ret;
-	int cmd_sz;
+	u8 addrs[4];
+	struct spi_mem_op op =
+			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
+				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
+				   SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
+				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
+	size_t remaining = len;
+	int ret;
 
 	/* get transfer protocols. */
-	inst_nbits = spi_nor_get_protocol_inst_nbits(nor->read_proto);
-	addr_nbits = spi_nor_get_protocol_addr_nbits(nor->read_proto);
-	data_nbits = spi_nor_get_protocol_data_nbits(nor->read_proto);
+	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 */
-	dummy = (dummy * addr_nbits) / 8;
-
-	if (spi_flash_read_supported(spi)) {
-		struct spi_flash_read_message msg;
+	op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
 
-		memset(&msg, 0, sizeof(msg));
+	while (remaining) {
+		m25p_offs2addr(nor, from, addrs);
 
-		msg.buf = buf;
-		msg.from = from;
-		msg.len = len;
-		msg.read_opcode = nor->read_opcode;
-		msg.addr_width = nor->addr_width;
-		msg.dummy_bytes = dummy;
-		msg.opcode_nbits = inst_nbits;
-		msg.addr_nbits = addr_nbits;
-		msg.data_nbits = data_nbits;
-
-		ret = spi_flash_read(spi, &msg);
-		if (ret < 0)
+		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+		ret = spi_mem_adjust_op_size(flash->spimem, &op);
+		if (ret)
 			return ret;
-		return msg.retlen;
-	}
-
-	spi_message_init(&m);
-	memset(t, 0, (sizeof t));
-
-	flash->command[0] = nor->read_opcode;
-	m25p_addr2cmd(nor, from, flash->command);
-
-	t[0].tx_buf = flash->command;
-	t[0].tx_nbits = inst_nbits;
-	t[0].len = m25p_cmdsz(nor) + dummy;
-	spi_message_add_tail(&t[0], &m);
-
-	/*
-	 * Set all dummy/mode cycle bits to avoid sending some manufacturer
-	 * specific pattern, which might make the memory enter its Continuous
-	 * Read mode by mistake.
-	 * Based on the different mode cycle bit patterns listed and described
-	 * in the JESD216B specification, the 0xff value works for all memories
-	 * and all manufacturers.
-	 */
-	cmd_sz = t[0].len;
-	memset(flash->command + cmd_sz - dummy, 0xff, dummy);
 
-	/* split the op code and address bytes into two transfers if needed. */
-	data_idx = 1;
-	if (addr_nbits != inst_nbits) {
-		t[0].len = 1;
-
-		t[1].tx_buf = &flash->command[1];
-		t[1].tx_nbits = addr_nbits;
-		t[1].len = cmd_sz - 1;
-		spi_message_add_tail(&t[1], &m);
+		ret = spi_mem_exec_op(flash->spimem, &op);
+		if (ret)
+			return ret;
 
-		data_idx = 2;
+		from += op.data.nbytes;
+		remaining -= op.data.nbytes;
+		op.data.buf.in += op.data.nbytes;
 	}
 
-	t[data_idx].rx_buf = buf;
-	t[data_idx].rx_nbits = data_nbits;
-	t[data_idx].len = min3(len, spi_max_transfer_size(spi),
-			       spi_max_message_size(spi) - cmd_sz);
-	spi_message_add_tail(&t[data_idx], &m);
-
-	ret = spi_sync(spi, &m);
-	if (ret)
-		return ret;
-
-	ret = m.actual_length - cmd_sz;
-	if (ret < 0)
-		return -EIO;
-	return ret;
+	return len;
 }
 
 /*
@@ -231,8 +164,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
  * matches what the READ command supports, at least until this driver
  * understands FAST_READ (for clocks over 25 MHz).
  */
-static int m25p_probe(struct spi_device *spi)
+static int m25p_probe(struct spi_mem *spimem)
 {
+	struct spi_device *spi = spimem->spi;
 	struct flash_platform_data	*data;
 	struct m25p *flash;
 	struct spi_nor *nor;
@@ -244,9 +178,9 @@ static int m25p_probe(struct spi_device *spi)
 	char *flash_name;
 	int ret;
 
-	data = dev_get_platdata(&spi->dev);
+	data = dev_get_platdata(&spimem->spi->dev);
 
-	flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
+	flash = devm_kzalloc(&spimem->spi->dev, sizeof(*flash), GFP_KERNEL);
 	if (!flash)
 		return -ENOMEM;
 
@@ -258,12 +192,12 @@ static int m25p_probe(struct spi_device *spi)
 	nor->write_reg = m25p80_write_reg;
 	nor->read_reg = m25p80_read_reg;
 
-	nor->dev = &spi->dev;
+	nor->dev = &spimem->spi->dev;
 	spi_nor_set_flash_node(nor, spi->dev.of_node);
 	nor->priv = flash;
 
 	spi_set_drvdata(spi, flash);
-	flash->spi = spi;
+	flash->spimem = spimem;
 
 	if (spi->mode & SPI_RX_QUAD) {
 		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
@@ -303,9 +237,9 @@ static int m25p_probe(struct spi_device *spi)
 }
 
 
-static int m25p_remove(struct spi_device *spi)
+static int m25p_remove(struct spi_mem *spimem)
 {
-	struct m25p	*flash = spi_get_drvdata(spi);
+	struct m25p	*flash = spi_mem_get_drvdata(spimem);
 
 	spi_nor_restore(&flash->spi_nor);
 
@@ -313,9 +247,9 @@ static int m25p_remove(struct spi_device *spi)
 	return mtd_device_unregister(&flash->spi_nor.mtd);
 }
 
-static void m25p_shutdown(struct spi_device *spi)
+static void m25p_shutdown(struct spi_mem *spimem)
 {
-	struct m25p *flash = spi_get_drvdata(spi);
+	struct m25p *flash = spi_mem_get_drvdata(spimem);
 
 	spi_nor_restore(&flash->spi_nor);
 }
@@ -386,12 +320,14 @@ static const struct of_device_id m25p_of_table[] = {
 };
 MODULE_DEVICE_TABLE(of, m25p_of_table);
 
-static struct spi_driver m25p80_driver = {
-	.driver = {
-		.name	= "m25p80",
-		.of_match_table = m25p_of_table,
+static struct spi_mem_driver m25p80_driver = {
+	.spidrv = {
+		.driver = {
+			.name	= "m25p80",
+			.of_match_table = m25p_of_table,
+		},
+		.id_table	= m25p_ids,
 	},
-	.id_table	= m25p_ids,
 	.probe	= m25p_probe,
 	.remove	= m25p_remove,
 	.shutdown	= m25p_shutdown,
@@ -402,7 +338,7 @@ static struct spi_driver m25p80_driver = {
 	 */
 };
 
-module_spi_driver(m25p80_driver);
+module_spi_mem_driver(m25p80_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Mike Lavender");
-- 
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API
@ 2018-02-05 23:21     ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

From: Boris Brezillon <boris.brezillon@free-electrons.com>

The spi_mem_xxx() API has been introduced to replace the
spi_flash_read() one. Make use of it so we can get rid of
spi_flash_read().

Note that using spi_mem_xx() also simplifies the code because this API
takes care of using the regular spi_sync() interface when the optimized
->mem_ops interface is not implemented by the controller.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/mtd/devices/m25p80.c | 240 ++++++++++++++++---------------------------
 1 file changed, 88 insertions(+), 152 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index a4e18f6aaa33..3b17425d5e6f 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -29,7 +29,7 @@
 
 #define	MAX_CMD_SIZE		6
 struct m25p {
-	struct spi_device	*spi;
+	struct spi_mem		*spimem;
 	struct spi_nor		spi_nor;
 	u8			command[MAX_CMD_SIZE];
 };
@@ -37,97 +37,80 @@ struct m25p {
 static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
+	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
+					  SPI_MEM_OP_NO_ADDRS,
+					  SPI_MEM_OP_NO_DUMMY,
+					  SPI_MEM_OP_DATA_IN(len, val, 1));
 	int ret;
 
-	ret = spi_write_then_read(spi, &code, 1, val, len);
+	ret = spi_mem_exec_op(flash->spimem, &op);
 	if (ret < 0)
-		dev_err(&spi->dev, "error %d reading %x\n", ret, code);
+		dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret,
+			code);
 
 	return ret;
 }
 
-static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
+static void m25p_offs2addr(struct spi_nor *nor, unsigned int offs, u8 *addrs)
 {
-	/* opcode is in cmd[0] */
-	cmd[1] = addr >> (nor->addr_width * 8 -  8);
-	cmd[2] = addr >> (nor->addr_width * 8 - 16);
-	cmd[3] = addr >> (nor->addr_width * 8 - 24);
-	cmd[4] = addr >> (nor->addr_width * 8 - 32);
-}
-
-static int m25p_cmdsz(struct spi_nor *nor)
-{
-	return 1 + nor->addr_width;
+	addrs[0] = offs >> (nor->addr_width * 8 -  8);
+	addrs[1] = offs >> (nor->addr_width * 8 - 16);
+	addrs[2] = offs >> (nor->addr_width * 8 - 24);
+	addrs[3] = offs >> (nor->addr_width * 8 - 32);
 }
 
 static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
+	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
+					  SPI_MEM_OP_NO_ADDRS,
+					  SPI_MEM_OP_NO_DUMMY,
+					  SPI_MEM_OP_DATA_OUT(len, buf, 1));
 
-	flash->command[0] = opcode;
-	if (buf)
-		memcpy(&flash->command[1], buf, len);
-
-	return spi_write(spi, flash->command, len + 1);
+	return spi_mem_exec_op(flash->spimem, &op);
 }
 
 static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
 			    const u_char *buf)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
-	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
-	struct spi_transfer t[3] = {};
-	struct spi_message m;
-	int cmd_sz = m25p_cmdsz(nor);
-	ssize_t ret;
+	u8 addrs[4];
+	struct spi_mem_op op =
+			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
+				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
+				   SPI_MEM_OP_DUMMY(0, 1),
+				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
+	size_t remaining = len;
+	int ret;
 
 	/* get transfer protocols. */
-	inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto);
-	addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto);
-	data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto);
-
-	spi_message_init(&m);
+	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)
-		cmd_sz = 1;
-
-	flash->command[0] = nor->program_opcode;
-	m25p_addr2cmd(nor, to, flash->command);
+		op.addr.nbytes = 0;
 
-	t[0].tx_buf = flash->command;
-	t[0].tx_nbits = inst_nbits;
-	t[0].len = cmd_sz;
-	spi_message_add_tail(&t[0], &m);
+	while (remaining) {
+		if (op.addr.nbytes)
+			m25p_offs2addr(nor, to, addrs);
 
-	/* split the op code and address bytes into two transfers if needed. */
-	data_idx = 1;
-	if (addr_nbits != inst_nbits) {
-		t[0].len = 1;
+		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+		ret = spi_mem_adjust_op_size(flash->spimem, &op);
+		if (ret)
+			return ret;
 
-		t[1].tx_buf = &flash->command[1];
-		t[1].tx_nbits = addr_nbits;
-		t[1].len = cmd_sz - 1;
-		spi_message_add_tail(&t[1], &m);
+		ret = spi_mem_exec_op(flash->spimem, &op);
+		if (ret)
+			return ret;
 
-		data_idx = 2;
+		to += op.data.nbytes;
+		remaining -= op.data.nbytes;
+		op.data.buf.out += op.data.nbytes;
 	}
 
-	t[data_idx].tx_buf = buf;
-	t[data_idx].tx_nbits = data_nbits;
-	t[data_idx].len = len;
-	spi_message_add_tail(&t[data_idx], &m);
-
-	ret = spi_sync(spi, &m);
-	if (ret)
-		return ret;
-
-	ret = m.actual_length - cmd_sz;
-	if (ret < 0)
-		return -EIO;
-	return ret;
+	return len;
 }
 
 /*
@@ -138,92 +121,42 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
 			   u_char *buf)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
-	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
-	struct spi_transfer t[3];
-	struct spi_message m;
-	unsigned int dummy = nor->read_dummy;
-	ssize_t ret;
-	int cmd_sz;
+	u8 addrs[4];
+	struct spi_mem_op op =
+			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
+				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
+				   SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
+				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
+	size_t remaining = len;
+	int ret;
 
 	/* get transfer protocols. */
-	inst_nbits = spi_nor_get_protocol_inst_nbits(nor->read_proto);
-	addr_nbits = spi_nor_get_protocol_addr_nbits(nor->read_proto);
-	data_nbits = spi_nor_get_protocol_data_nbits(nor->read_proto);
+	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 */
-	dummy = (dummy * addr_nbits) / 8;
-
-	if (spi_flash_read_supported(spi)) {
-		struct spi_flash_read_message msg;
+	op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
 
-		memset(&msg, 0, sizeof(msg));
+	while (remaining) {
+		m25p_offs2addr(nor, from, addrs);
 
-		msg.buf = buf;
-		msg.from = from;
-		msg.len = len;
-		msg.read_opcode = nor->read_opcode;
-		msg.addr_width = nor->addr_width;
-		msg.dummy_bytes = dummy;
-		msg.opcode_nbits = inst_nbits;
-		msg.addr_nbits = addr_nbits;
-		msg.data_nbits = data_nbits;
-
-		ret = spi_flash_read(spi, &msg);
-		if (ret < 0)
+		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+		ret = spi_mem_adjust_op_size(flash->spimem, &op);
+		if (ret)
 			return ret;
-		return msg.retlen;
-	}
-
-	spi_message_init(&m);
-	memset(t, 0, (sizeof t));
-
-	flash->command[0] = nor->read_opcode;
-	m25p_addr2cmd(nor, from, flash->command);
-
-	t[0].tx_buf = flash->command;
-	t[0].tx_nbits = inst_nbits;
-	t[0].len = m25p_cmdsz(nor) + dummy;
-	spi_message_add_tail(&t[0], &m);
-
-	/*
-	 * Set all dummy/mode cycle bits to avoid sending some manufacturer
-	 * specific pattern, which might make the memory enter its Continuous
-	 * Read mode by mistake.
-	 * Based on the different mode cycle bit patterns listed and described
-	 * in the JESD216B specification, the 0xff value works for all memories
-	 * and all manufacturers.
-	 */
-	cmd_sz = t[0].len;
-	memset(flash->command + cmd_sz - dummy, 0xff, dummy);
 
-	/* split the op code and address bytes into two transfers if needed. */
-	data_idx = 1;
-	if (addr_nbits != inst_nbits) {
-		t[0].len = 1;
-
-		t[1].tx_buf = &flash->command[1];
-		t[1].tx_nbits = addr_nbits;
-		t[1].len = cmd_sz - 1;
-		spi_message_add_tail(&t[1], &m);
+		ret = spi_mem_exec_op(flash->spimem, &op);
+		if (ret)
+			return ret;
 
-		data_idx = 2;
+		from += op.data.nbytes;
+		remaining -= op.data.nbytes;
+		op.data.buf.in += op.data.nbytes;
 	}
 
-	t[data_idx].rx_buf = buf;
-	t[data_idx].rx_nbits = data_nbits;
-	t[data_idx].len = min3(len, spi_max_transfer_size(spi),
-			       spi_max_message_size(spi) - cmd_sz);
-	spi_message_add_tail(&t[data_idx], &m);
-
-	ret = spi_sync(spi, &m);
-	if (ret)
-		return ret;
-
-	ret = m.actual_length - cmd_sz;
-	if (ret < 0)
-		return -EIO;
-	return ret;
+	return len;
 }
 
 /*
@@ -231,8 +164,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
  * matches what the READ command supports, at least until this driver
  * understands FAST_READ (for clocks over 25 MHz).
  */
-static int m25p_probe(struct spi_device *spi)
+static int m25p_probe(struct spi_mem *spimem)
 {
+	struct spi_device *spi = spimem->spi;
 	struct flash_platform_data	*data;
 	struct m25p *flash;
 	struct spi_nor *nor;
@@ -244,9 +178,9 @@ static int m25p_probe(struct spi_device *spi)
 	char *flash_name;
 	int ret;
 
-	data = dev_get_platdata(&spi->dev);
+	data = dev_get_platdata(&spimem->spi->dev);
 
-	flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
+	flash = devm_kzalloc(&spimem->spi->dev, sizeof(*flash), GFP_KERNEL);
 	if (!flash)
 		return -ENOMEM;
 
@@ -258,12 +192,12 @@ static int m25p_probe(struct spi_device *spi)
 	nor->write_reg = m25p80_write_reg;
 	nor->read_reg = m25p80_read_reg;
 
-	nor->dev = &spi->dev;
+	nor->dev = &spimem->spi->dev;
 	spi_nor_set_flash_node(nor, spi->dev.of_node);
 	nor->priv = flash;
 
 	spi_set_drvdata(spi, flash);
-	flash->spi = spi;
+	flash->spimem = spimem;
 
 	if (spi->mode & SPI_RX_QUAD) {
 		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
@@ -303,9 +237,9 @@ static int m25p_probe(struct spi_device *spi)
 }
 
 
-static int m25p_remove(struct spi_device *spi)
+static int m25p_remove(struct spi_mem *spimem)
 {
-	struct m25p	*flash = spi_get_drvdata(spi);
+	struct m25p	*flash = spi_mem_get_drvdata(spimem);
 
 	spi_nor_restore(&flash->spi_nor);
 
@@ -313,9 +247,9 @@ static int m25p_remove(struct spi_device *spi)
 	return mtd_device_unregister(&flash->spi_nor.mtd);
 }
 
-static void m25p_shutdown(struct spi_device *spi)
+static void m25p_shutdown(struct spi_mem *spimem)
 {
-	struct m25p *flash = spi_get_drvdata(spi);
+	struct m25p *flash = spi_mem_get_drvdata(spimem);
 
 	spi_nor_restore(&flash->spi_nor);
 }
@@ -386,12 +320,14 @@ static const struct of_device_id m25p_of_table[] = {
 };
 MODULE_DEVICE_TABLE(of, m25p_of_table);
 
-static struct spi_driver m25p80_driver = {
-	.driver = {
-		.name	= "m25p80",
-		.of_match_table = m25p_of_table,
+static struct spi_mem_driver m25p80_driver = {
+	.spidrv = {
+		.driver = {
+			.name	= "m25p80",
+			.of_match_table = m25p_of_table,
+		},
+		.id_table	= m25p_ids,
 	},
-	.id_table	= m25p_ids,
 	.probe	= m25p_probe,
 	.remove	= m25p_remove,
 	.shutdown	= m25p_shutdown,
@@ -402,7 +338,7 @@ static struct spi_driver m25p80_driver = {
 	 */
 };
 
-module_spi_driver(m25p80_driver);
+module_spi_mem_driver(m25p80_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Mike Lavender");
-- 
2.14.1

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

* [RFC PATCH 6/6] spi: Get rid of the spi_flash_read() API
  2018-02-05 23:21 ` Boris Brezillon
@ 2018-02-05 23:21     ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

This API has been replaced by the spi_mem_xx() one, its only user
(spi-nor) has been converted to spi_mem_xx() and all SPI controller
drivers that were implementing the ->spi_flash_xxx() hooks are also
implementing the spi_mem ones. So we can safely get rid of this API.

Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/spi/spi-bcm-qspi.c | 38 +++----------------------------
 drivers/spi/spi-bcm53xx.c  | 20 +---------------
 drivers/spi/spi-ti-qspi.c  | 41 ---------------------------------
 drivers/spi/spi.c          | 57 ----------------------------------------------
 include/linux/spi/spi.h    | 53 ------------------------------------------
 5 files changed, 4 insertions(+), 205 deletions(-)

diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c
index f47a13453ef6..4670fe5d83f8 100644
--- a/drivers/spi/spi-bcm-qspi.c
+++ b/drivers/spi/spi-bcm-qspi.c
@@ -946,9 +946,10 @@ static int bcm_qspi_mspi_exec_mem_op(struct spi_device *spi,
 	return ret;
 }
 
-static int bcm_qspi_exec_mem_op(struct spi_device *spi,
+static int bcm_qspi_exec_mem_op(struct spi_mem *mem,
 				const struct spi_mem_op *op)
 {
+	struct spi_device *spi = mem->spi;
 	struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
 	int ret = 0, i;
 	bool mspi_read = false;
@@ -998,38 +999,6 @@ static int bcm_qspi_exec_mem_op(struct spi_device *spi,
 	return ret;
 }
 
-static int bcm_qspi_exec_mem_op_wrapper(struct spi_mem *mem,
-					const struct spi_mem_op *op)
-{
-	return bcm_qspi_exec_mem_op(mem->spi, op);
-}
-
-static int bcm_qspi_flash_read_wrapper(struct spi_device *spi,
-				       struct spi_flash_read_message *msg)
-{
-	int i, ret;
-	u8 addrs[4];
-	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(msg->read_opcode, 1),
-					  SPI_MEM_OP_ADDRS(msg->addr_width,
-							   addrs,
-							   msg->addr_nbits),
-					  SPI_MEM_OP_DUMMY(msg->dummy_bytes,
-							   msg->addr_nbits),
-					  SPI_MEM_OP_DATA_IN(msg->len,
-							     msg->buf,
-							     msg->data_nbits));
-
-	for (i = msg->addr_width - 1; i >= 0; i--)
-		addrs[i] = msg->from >> (i * 8);
-
-	msg->retlen = 0;
-	ret = bcm_qspi_exec_mem_op(spi, &op);
-	if (!ret)
-		msg->retlen = msg->len;
-
-	return ret;
-}
-
 static void bcm_qspi_cleanup(struct spi_device *spi)
 {
 	struct bcm_qspi_parms *xp = spi_get_ctldata(spi);
@@ -1225,7 +1194,7 @@ static void bcm_qspi_hw_uninit(struct bcm_qspi *qspi)
 }
 
 static const struct spi_controller_mem_ops bcm_qspi_mem_ops = {
-	.exec_op = bcm_qspi_exec_mem_op_wrapper,
+	.exec_op = bcm_qspi_exec_mem_op,
 };
 
 static const struct of_device_id bcm_qspi_of_match[] = {
@@ -1270,7 +1239,6 @@ int bcm_qspi_probe(struct platform_device *pdev,
 	master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD;
 	master->setup = bcm_qspi_setup;
 	master->transfer_one = bcm_qspi_transfer_one;
-	master->spi_flash_read = bcm_qspi_flash_read_wrapper;
 	master->mem_ops = &bcm_qspi_mem_ops;
 	master->cleanup = bcm_qspi_cleanup;
 	master->dev.of_node = dev->of_node;
diff --git a/drivers/spi/spi-bcm53xx.c b/drivers/spi/spi-bcm53xx.c
index c681d432e26c..707213418bb3 100644
--- a/drivers/spi/spi-bcm53xx.c
+++ b/drivers/spi/spi-bcm53xx.c
@@ -297,22 +297,6 @@ static const struct spi_controller_mem_ops bcm53xxspi_mem_ops = {
 	.exec_op = bcm53xxspi_exec_mem_op,
 };
 
-static int bcm53xxspi_flash_read(struct spi_device *spi,
-				 struct spi_flash_read_message *msg)
-{
-	struct bcm53xxspi *b53spi = spi_master_get_devdata(spi->master);
-	int ret = 0;
-
-	if (msg->from + msg->len > BCM53XXSPI_FLASH_WINDOW)
-		return -EINVAL;
-
-	bcm53xxspi_enable_bspi(b53spi);
-	memcpy_fromio(msg->buf, b53spi->mmio_base + msg->from, msg->len);
-	msg->retlen = msg->len;
-
-	return ret;
-}
-
 /**************************************************
  * BCMA
  **************************************************/
@@ -351,10 +335,8 @@ static int bcm53xxspi_bcma_probe(struct bcma_device *core)
 
 	master->dev.of_node = dev->of_node;
 	master->transfer_one = bcm53xxspi_transfer_one;
-	if (b53spi->mmio_base) {
+	if (b53spi->mmio_base)
 		master->mem_ops = &bcm53xxspi_mem_ops;
-		master->spi_flash_read = bcm53xxspi_flash_read;
-	}
 
 	bcma_set_drvdata(core, b53spi);
 
diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
index 40cac3ef6cc9..50237e2753b2 100644
--- a/drivers/spi/spi-ti-qspi.c
+++ b/drivers/spi/spi-ti-qspi.c
@@ -529,44 +529,6 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi, u8 opcode,
 		      QSPI_SPI_SETUP_REG(spi->chip_select));
 }
 
-static bool ti_qspi_spi_flash_can_dma(struct spi_device *spi,
-				      struct spi_flash_read_message *msg)
-{
-	return virt_addr_valid(msg->buf);
-}
-
-static int ti_qspi_spi_flash_read(struct spi_device *spi,
-				  struct spi_flash_read_message *msg)
-{
-	struct ti_qspi *qspi = spi_master_get_devdata(spi->master);
-	int ret = 0;
-
-	mutex_lock(&qspi->list_lock);
-
-	if (!qspi->mmap_enabled)
-		ti_qspi_enable_memory_map(spi);
-	ti_qspi_setup_mmap_read(spi, msg->read_opcode, msg->data_nbits,
-				msg->addr_width, msg->dummy_bytes);
-
-	if (qspi->rx_chan) {
-		if (msg->cur_msg_mapped)
-			ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from);
-		else
-			ret = ti_qspi_dma_bounce_buffer(qspi, msg->from,
-							msg->buf, msg->len);
-		if (ret)
-			goto err_unlock;
-	} else {
-		memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len);
-	}
-	msg->retlen = msg->len;
-
-err_unlock:
-	mutex_unlock(&qspi->list_lock);
-
-	return ret;
-}
-
 static int ti_qspi_exec_mem_op(struct spi_mem *mem,
 			       const struct spi_mem_op *op)
 {
@@ -729,7 +691,6 @@ static int ti_qspi_probe(struct platform_device *pdev)
 	master->dev.of_node = pdev->dev.of_node;
 	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
 				     SPI_BPW_MASK(8);
-	master->spi_flash_read = ti_qspi_spi_flash_read;
 	master->mem_ops = &ti_qspi_mem_ops;
 
 	if (!of_property_read_u32(np, "num-cs", &num_cs))
@@ -828,7 +789,6 @@ static int ti_qspi_probe(struct platform_device *pdev)
 		dma_release_channel(qspi->rx_chan);
 		goto no_dma;
 	}
-	master->spi_flash_can_dma = ti_qspi_spi_flash_can_dma;
 	master->dma_rx = qspi->rx_chan;
 	init_completion(&qspi->transfer_complete);
 	if (res_mmap)
@@ -842,7 +802,6 @@ static int ti_qspi_probe(struct platform_device *pdev)
 				 "mmap failed with error %ld using PIO mode\n",
 				 PTR_ERR(qspi->mmap_base));
 			qspi->mmap_base = NULL;
-			master->spi_flash_read = NULL;
 			master->mem_ops = NULL;
 		}
 	}
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 57bc540a0521..0491ef8c321f 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -3046,63 +3046,6 @@ int spi_async_locked(struct spi_device *spi, struct spi_message *message)
 }
 EXPORT_SYMBOL_GPL(spi_async_locked);
 
-
-int spi_flash_read(struct spi_device *spi,
-		   struct spi_flash_read_message *msg)
-
-{
-	struct spi_controller *master = spi->controller;
-	struct device *rx_dev = NULL;
-	int ret;
-
-	if ((msg->opcode_nbits == SPI_NBITS_DUAL ||
-	     msg->addr_nbits == SPI_NBITS_DUAL) &&
-	    !(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD)))
-		return -EINVAL;
-	if ((msg->opcode_nbits == SPI_NBITS_QUAD ||
-	     msg->addr_nbits == SPI_NBITS_QUAD) &&
-	    !(spi->mode & SPI_TX_QUAD))
-		return -EINVAL;
-	if (msg->data_nbits == SPI_NBITS_DUAL &&
-	    !(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD)))
-		return -EINVAL;
-	if (msg->data_nbits == SPI_NBITS_QUAD &&
-	    !(spi->mode &  SPI_RX_QUAD))
-		return -EINVAL;
-
-	if (master->auto_runtime_pm) {
-		ret = pm_runtime_get_sync(master->dev.parent);
-		if (ret < 0) {
-			dev_err(&master->dev, "Failed to power device: %d\n",
-				ret);
-			return ret;
-		}
-	}
-
-	mutex_lock(&master->bus_lock_mutex);
-	mutex_lock(&master->io_mutex);
-	if (master->dma_rx && master->spi_flash_can_dma(spi, msg)) {
-		rx_dev = master->dma_rx->device->dev;
-		ret = spi_map_buf(master, rx_dev, &msg->rx_sg,
-				  msg->buf, msg->len,
-				  DMA_FROM_DEVICE);
-		if (!ret)
-			msg->cur_msg_mapped = true;
-	}
-	ret = master->spi_flash_read(spi, msg);
-	if (msg->cur_msg_mapped)
-		spi_unmap_buf(master, rx_dev, &msg->rx_sg,
-			      DMA_FROM_DEVICE);
-	mutex_unlock(&master->io_mutex);
-	mutex_unlock(&master->bus_lock_mutex);
-
-	if (master->auto_runtime_pm)
-		pm_runtime_put(master->dev.parent);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(spi_flash_read);
-
 /*-------------------------------------------------------------------------*/
 
 /* Utility methods for SPI protocol drivers, layered on
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index af3c4ac62b55..0921d2c82b35 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -26,7 +26,6 @@ struct dma_chan;
 struct property_entry;
 struct spi_controller;
 struct spi_transfer;
-struct spi_flash_read_message;
 struct spi_controller_mem_ops;
 
 /*
@@ -382,11 +381,6 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
  *	     controller has native support for memory like operations.
  * @unprepare_message: undo any work done by prepare_message().
  * @slave_abort: abort the ongoing transfer request on an SPI slave controller
- * @spi_flash_read: to support spi-controller hardwares that provide
- *                  accelerated interface to read from flash devices.
- * @spi_flash_can_dma: analogous to can_dma() interface, but for
- *		       controllers implementing spi_flash_read.
- * @flash_read_supported: spi device supports flash read
  * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
  *	number. Any individual value may be -ENOENT for CS lines that
  *	are not GPIOs (driven by the SPI controller itself).
@@ -552,11 +546,6 @@ struct spi_controller {
 	int (*unprepare_message)(struct spi_controller *ctlr,
 				 struct spi_message *message);
 	int (*slave_abort)(struct spi_controller *ctlr);
-	int (*spi_flash_read)(struct  spi_device *spi,
-			      struct spi_flash_read_message *msg);
-	bool (*spi_flash_can_dma)(struct spi_device *spi,
-				  struct spi_flash_read_message *msg);
-	bool (*flash_read_supported)(struct spi_device *spi);
 
 	/*
 	 * These hooks are for drivers that use a generic implementation
@@ -1190,48 +1179,6 @@ static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd)
 	return be16_to_cpu(result);
 }
 
-/**
- * struct spi_flash_read_message - flash specific information for
- * spi-masters that provide accelerated flash read interfaces
- * @buf: buffer to read data
- * @from: offset within the flash from where data is to be read
- * @len: length of data to be read
- * @retlen: actual length of data read
- * @read_opcode: read_opcode to be used to communicate with flash
- * @addr_width: number of address bytes
- * @dummy_bytes: number of dummy bytes
- * @opcode_nbits: number of lines to send opcode
- * @addr_nbits: number of lines to send address
- * @data_nbits: number of lines for data
- * @rx_sg: Scatterlist for receive data read from flash
- * @cur_msg_mapped: message has been mapped for DMA
- */
-struct spi_flash_read_message {
-	void *buf;
-	loff_t from;
-	size_t len;
-	size_t retlen;
-	u8 read_opcode;
-	u8 addr_width;
-	u8 dummy_bytes;
-	u8 opcode_nbits;
-	u8 addr_nbits;
-	u8 data_nbits;
-	struct sg_table rx_sg;
-	bool cur_msg_mapped;
-};
-
-/* SPI core interface for flash read support */
-static inline bool spi_flash_read_supported(struct spi_device *spi)
-{
-	return spi->controller->spi_flash_read &&
-	       (!spi->controller->flash_read_supported ||
-	       spi->controller->flash_read_supported(spi));
-}
-
-int spi_flash_read(struct spi_device *spi,
-		   struct spi_flash_read_message *msg);
-
 /*---------------------------------------------------------------------------*/
 
 /* SPI memory related definitions. */
-- 
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [RFC PATCH 6/6] spi: Get rid of the spi_flash_read() API
@ 2018-02-05 23:21     ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-05 23:21 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

From: Boris Brezillon <boris.brezillon@free-electrons.com>

This API has been replaced by the spi_mem_xx() one, its only user
(spi-nor) has been converted to spi_mem_xx() and all SPI controller
drivers that were implementing the ->spi_flash_xxx() hooks are also
implementing the spi_mem ones. So we can safely get rid of this API.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/spi/spi-bcm-qspi.c | 38 +++----------------------------
 drivers/spi/spi-bcm53xx.c  | 20 +---------------
 drivers/spi/spi-ti-qspi.c  | 41 ---------------------------------
 drivers/spi/spi.c          | 57 ----------------------------------------------
 include/linux/spi/spi.h    | 53 ------------------------------------------
 5 files changed, 4 insertions(+), 205 deletions(-)

diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c
index f47a13453ef6..4670fe5d83f8 100644
--- a/drivers/spi/spi-bcm-qspi.c
+++ b/drivers/spi/spi-bcm-qspi.c
@@ -946,9 +946,10 @@ static int bcm_qspi_mspi_exec_mem_op(struct spi_device *spi,
 	return ret;
 }
 
-static int bcm_qspi_exec_mem_op(struct spi_device *spi,
+static int bcm_qspi_exec_mem_op(struct spi_mem *mem,
 				const struct spi_mem_op *op)
 {
+	struct spi_device *spi = mem->spi;
 	struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
 	int ret = 0, i;
 	bool mspi_read = false;
@@ -998,38 +999,6 @@ static int bcm_qspi_exec_mem_op(struct spi_device *spi,
 	return ret;
 }
 
-static int bcm_qspi_exec_mem_op_wrapper(struct spi_mem *mem,
-					const struct spi_mem_op *op)
-{
-	return bcm_qspi_exec_mem_op(mem->spi, op);
-}
-
-static int bcm_qspi_flash_read_wrapper(struct spi_device *spi,
-				       struct spi_flash_read_message *msg)
-{
-	int i, ret;
-	u8 addrs[4];
-	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(msg->read_opcode, 1),
-					  SPI_MEM_OP_ADDRS(msg->addr_width,
-							   addrs,
-							   msg->addr_nbits),
-					  SPI_MEM_OP_DUMMY(msg->dummy_bytes,
-							   msg->addr_nbits),
-					  SPI_MEM_OP_DATA_IN(msg->len,
-							     msg->buf,
-							     msg->data_nbits));
-
-	for (i = msg->addr_width - 1; i >= 0; i--)
-		addrs[i] = msg->from >> (i * 8);
-
-	msg->retlen = 0;
-	ret = bcm_qspi_exec_mem_op(spi, &op);
-	if (!ret)
-		msg->retlen = msg->len;
-
-	return ret;
-}
-
 static void bcm_qspi_cleanup(struct spi_device *spi)
 {
 	struct bcm_qspi_parms *xp = spi_get_ctldata(spi);
@@ -1225,7 +1194,7 @@ static void bcm_qspi_hw_uninit(struct bcm_qspi *qspi)
 }
 
 static const struct spi_controller_mem_ops bcm_qspi_mem_ops = {
-	.exec_op = bcm_qspi_exec_mem_op_wrapper,
+	.exec_op = bcm_qspi_exec_mem_op,
 };
 
 static const struct of_device_id bcm_qspi_of_match[] = {
@@ -1270,7 +1239,6 @@ int bcm_qspi_probe(struct platform_device *pdev,
 	master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD;
 	master->setup = bcm_qspi_setup;
 	master->transfer_one = bcm_qspi_transfer_one;
-	master->spi_flash_read = bcm_qspi_flash_read_wrapper;
 	master->mem_ops = &bcm_qspi_mem_ops;
 	master->cleanup = bcm_qspi_cleanup;
 	master->dev.of_node = dev->of_node;
diff --git a/drivers/spi/spi-bcm53xx.c b/drivers/spi/spi-bcm53xx.c
index c681d432e26c..707213418bb3 100644
--- a/drivers/spi/spi-bcm53xx.c
+++ b/drivers/spi/spi-bcm53xx.c
@@ -297,22 +297,6 @@ static const struct spi_controller_mem_ops bcm53xxspi_mem_ops = {
 	.exec_op = bcm53xxspi_exec_mem_op,
 };
 
-static int bcm53xxspi_flash_read(struct spi_device *spi,
-				 struct spi_flash_read_message *msg)
-{
-	struct bcm53xxspi *b53spi = spi_master_get_devdata(spi->master);
-	int ret = 0;
-
-	if (msg->from + msg->len > BCM53XXSPI_FLASH_WINDOW)
-		return -EINVAL;
-
-	bcm53xxspi_enable_bspi(b53spi);
-	memcpy_fromio(msg->buf, b53spi->mmio_base + msg->from, msg->len);
-	msg->retlen = msg->len;
-
-	return ret;
-}
-
 /**************************************************
  * BCMA
  **************************************************/
@@ -351,10 +335,8 @@ static int bcm53xxspi_bcma_probe(struct bcma_device *core)
 
 	master->dev.of_node = dev->of_node;
 	master->transfer_one = bcm53xxspi_transfer_one;
-	if (b53spi->mmio_base) {
+	if (b53spi->mmio_base)
 		master->mem_ops = &bcm53xxspi_mem_ops;
-		master->spi_flash_read = bcm53xxspi_flash_read;
-	}
 
 	bcma_set_drvdata(core, b53spi);
 
diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
index 40cac3ef6cc9..50237e2753b2 100644
--- a/drivers/spi/spi-ti-qspi.c
+++ b/drivers/spi/spi-ti-qspi.c
@@ -529,44 +529,6 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi, u8 opcode,
 		      QSPI_SPI_SETUP_REG(spi->chip_select));
 }
 
-static bool ti_qspi_spi_flash_can_dma(struct spi_device *spi,
-				      struct spi_flash_read_message *msg)
-{
-	return virt_addr_valid(msg->buf);
-}
-
-static int ti_qspi_spi_flash_read(struct spi_device *spi,
-				  struct spi_flash_read_message *msg)
-{
-	struct ti_qspi *qspi = spi_master_get_devdata(spi->master);
-	int ret = 0;
-
-	mutex_lock(&qspi->list_lock);
-
-	if (!qspi->mmap_enabled)
-		ti_qspi_enable_memory_map(spi);
-	ti_qspi_setup_mmap_read(spi, msg->read_opcode, msg->data_nbits,
-				msg->addr_width, msg->dummy_bytes);
-
-	if (qspi->rx_chan) {
-		if (msg->cur_msg_mapped)
-			ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from);
-		else
-			ret = ti_qspi_dma_bounce_buffer(qspi, msg->from,
-							msg->buf, msg->len);
-		if (ret)
-			goto err_unlock;
-	} else {
-		memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len);
-	}
-	msg->retlen = msg->len;
-
-err_unlock:
-	mutex_unlock(&qspi->list_lock);
-
-	return ret;
-}
-
 static int ti_qspi_exec_mem_op(struct spi_mem *mem,
 			       const struct spi_mem_op *op)
 {
@@ -729,7 +691,6 @@ static int ti_qspi_probe(struct platform_device *pdev)
 	master->dev.of_node = pdev->dev.of_node;
 	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
 				     SPI_BPW_MASK(8);
-	master->spi_flash_read = ti_qspi_spi_flash_read;
 	master->mem_ops = &ti_qspi_mem_ops;
 
 	if (!of_property_read_u32(np, "num-cs", &num_cs))
@@ -828,7 +789,6 @@ static int ti_qspi_probe(struct platform_device *pdev)
 		dma_release_channel(qspi->rx_chan);
 		goto no_dma;
 	}
-	master->spi_flash_can_dma = ti_qspi_spi_flash_can_dma;
 	master->dma_rx = qspi->rx_chan;
 	init_completion(&qspi->transfer_complete);
 	if (res_mmap)
@@ -842,7 +802,6 @@ static int ti_qspi_probe(struct platform_device *pdev)
 				 "mmap failed with error %ld using PIO mode\n",
 				 PTR_ERR(qspi->mmap_base));
 			qspi->mmap_base = NULL;
-			master->spi_flash_read = NULL;
 			master->mem_ops = NULL;
 		}
 	}
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 57bc540a0521..0491ef8c321f 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -3046,63 +3046,6 @@ int spi_async_locked(struct spi_device *spi, struct spi_message *message)
 }
 EXPORT_SYMBOL_GPL(spi_async_locked);
 
-
-int spi_flash_read(struct spi_device *spi,
-		   struct spi_flash_read_message *msg)
-
-{
-	struct spi_controller *master = spi->controller;
-	struct device *rx_dev = NULL;
-	int ret;
-
-	if ((msg->opcode_nbits == SPI_NBITS_DUAL ||
-	     msg->addr_nbits == SPI_NBITS_DUAL) &&
-	    !(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD)))
-		return -EINVAL;
-	if ((msg->opcode_nbits == SPI_NBITS_QUAD ||
-	     msg->addr_nbits == SPI_NBITS_QUAD) &&
-	    !(spi->mode & SPI_TX_QUAD))
-		return -EINVAL;
-	if (msg->data_nbits == SPI_NBITS_DUAL &&
-	    !(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD)))
-		return -EINVAL;
-	if (msg->data_nbits == SPI_NBITS_QUAD &&
-	    !(spi->mode &  SPI_RX_QUAD))
-		return -EINVAL;
-
-	if (master->auto_runtime_pm) {
-		ret = pm_runtime_get_sync(master->dev.parent);
-		if (ret < 0) {
-			dev_err(&master->dev, "Failed to power device: %d\n",
-				ret);
-			return ret;
-		}
-	}
-
-	mutex_lock(&master->bus_lock_mutex);
-	mutex_lock(&master->io_mutex);
-	if (master->dma_rx && master->spi_flash_can_dma(spi, msg)) {
-		rx_dev = master->dma_rx->device->dev;
-		ret = spi_map_buf(master, rx_dev, &msg->rx_sg,
-				  msg->buf, msg->len,
-				  DMA_FROM_DEVICE);
-		if (!ret)
-			msg->cur_msg_mapped = true;
-	}
-	ret = master->spi_flash_read(spi, msg);
-	if (msg->cur_msg_mapped)
-		spi_unmap_buf(master, rx_dev, &msg->rx_sg,
-			      DMA_FROM_DEVICE);
-	mutex_unlock(&master->io_mutex);
-	mutex_unlock(&master->bus_lock_mutex);
-
-	if (master->auto_runtime_pm)
-		pm_runtime_put(master->dev.parent);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(spi_flash_read);
-
 /*-------------------------------------------------------------------------*/
 
 /* Utility methods for SPI protocol drivers, layered on
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index af3c4ac62b55..0921d2c82b35 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -26,7 +26,6 @@ struct dma_chan;
 struct property_entry;
 struct spi_controller;
 struct spi_transfer;
-struct spi_flash_read_message;
 struct spi_controller_mem_ops;
 
 /*
@@ -382,11 +381,6 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
  *	     controller has native support for memory like operations.
  * @unprepare_message: undo any work done by prepare_message().
  * @slave_abort: abort the ongoing transfer request on an SPI slave controller
- * @spi_flash_read: to support spi-controller hardwares that provide
- *                  accelerated interface to read from flash devices.
- * @spi_flash_can_dma: analogous to can_dma() interface, but for
- *		       controllers implementing spi_flash_read.
- * @flash_read_supported: spi device supports flash read
  * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
  *	number. Any individual value may be -ENOENT for CS lines that
  *	are not GPIOs (driven by the SPI controller itself).
@@ -552,11 +546,6 @@ struct spi_controller {
 	int (*unprepare_message)(struct spi_controller *ctlr,
 				 struct spi_message *message);
 	int (*slave_abort)(struct spi_controller *ctlr);
-	int (*spi_flash_read)(struct  spi_device *spi,
-			      struct spi_flash_read_message *msg);
-	bool (*spi_flash_can_dma)(struct spi_device *spi,
-				  struct spi_flash_read_message *msg);
-	bool (*flash_read_supported)(struct spi_device *spi);
 
 	/*
 	 * These hooks are for drivers that use a generic implementation
@@ -1190,48 +1179,6 @@ static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd)
 	return be16_to_cpu(result);
 }
 
-/**
- * struct spi_flash_read_message - flash specific information for
- * spi-masters that provide accelerated flash read interfaces
- * @buf: buffer to read data
- * @from: offset within the flash from where data is to be read
- * @len: length of data to be read
- * @retlen: actual length of data read
- * @read_opcode: read_opcode to be used to communicate with flash
- * @addr_width: number of address bytes
- * @dummy_bytes: number of dummy bytes
- * @opcode_nbits: number of lines to send opcode
- * @addr_nbits: number of lines to send address
- * @data_nbits: number of lines for data
- * @rx_sg: Scatterlist for receive data read from flash
- * @cur_msg_mapped: message has been mapped for DMA
- */
-struct spi_flash_read_message {
-	void *buf;
-	loff_t from;
-	size_t len;
-	size_t retlen;
-	u8 read_opcode;
-	u8 addr_width;
-	u8 dummy_bytes;
-	u8 opcode_nbits;
-	u8 addr_nbits;
-	u8 data_nbits;
-	struct sg_table rx_sg;
-	bool cur_msg_mapped;
-};
-
-/* SPI core interface for flash read support */
-static inline bool spi_flash_read_supported(struct spi_device *spi)
-{
-	return spi->controller->spi_flash_read &&
-	       (!spi->controller->flash_read_supported ||
-	       spi->controller->flash_read_supported(spi));
-}
-
-int spi_flash_read(struct spi_device *spi,
-		   struct spi_flash_read_message *msg);
-
 /*---------------------------------------------------------------------------*/
 
 /* SPI memory related definitions. */
-- 
2.14.1

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-05 23:21     ` Boris Brezillon
@ 2018-02-06  9:43         ` Maxime Chevallier
  -1 siblings, 0 replies; 92+ messages in thread
From: Maxime Chevallier @ 2018-02-06  9:43 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Frieder Schrempf,
	Vignesh R, Yogesh Gaur, Rafał Miłecki, Kamal Dasu,
	Sourav Poddar

Hi Boris,

> From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> 
> Some controllers are exposing high-level interfaces to access various
> kind of SPI memories. Unfortunately they do not fit in the current
> spi_controller model and usually have drivers placed in
> drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> memories in general.
> 
> This is an attempt at defining a SPI memory interface which works for
> all kinds of SPI memories (NORs, NANDs, SRAMs).
> 
> Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> ---
>  drivers/spi/spi.c       | 423
> +++++++++++++++++++++++++++++++++++++++++++++++-
> include/linux/spi/spi.h | 226 ++++++++++++++++++++++++++ 2 files
> changed, 646 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index b33a727a0158..57bc540a0521 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -2057,6 +2057,24 @@ static int of_spi_register_master(struct
> spi_controller *ctlr) }
>  #endif
>  

[...]

> +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> +	struct spi_controller *ctlr = mem->spi->controller;
> +	struct spi_transfer xfers[4] = { };
> +	struct spi_message msg;
> +	u8 *tmpbuf;
> +	int ret;
> +
> +	if (!spi_mem_supports_op(mem, op))
> +		return -ENOTSUPP;
> +
> +	if (ctlr->mem_ops) {
> +		if (ctlr->auto_runtime_pm) {
> +			ret = pm_runtime_get_sync(ctlr->dev.parent);
> +			if (ret < 0) {
> +				dev_err(&ctlr->dev,
> +					"Failed to power device:
> %d\n",
> +					ret);
> +				return ret;
> +			}
> +		}
> +
> +		mutex_lock(&ctlr->bus_lock_mutex);
> +		mutex_lock(&ctlr->io_mutex);
> +		ret = ctlr->mem_ops->exec_op(mem, op);

As a user, what prevented me from using spi_flash_read is that it
bypasses the message queue. I have a setup that uses spi_async and I
have to make sure everything goes in the right order, so I ended up
using spi_write_then_read instead.

Is there a way to make so that the message that uses exec_op are issued
in the correct order regarding messages that are already queued ?

Maybe we could extend spi_message or spi_transfer to store all
this opcode/dummy/addr information, so that we would use the standard
interfaces spi_sync / spi_async, and make this mechanism of exec_op
transparent from the user ?


> +		mutex_unlock(&ctlr->io_mutex);
> +		mutex_unlock(&ctlr->bus_lock_mutex);
> +
> +		if (ctlr->auto_runtime_pm)
> +			pm_runtime_put(ctlr->dev.parent);
> +
> +		/*
> +		 * Some controllers only optimize specific paths
> (typically the
> +		 * read path) and expect the core to use the regular
> SPI
> +		 * interface in these cases.
> +		 */
> +		if (!ret || ret != -ENOTSUPP)
> +			return ret;
> +	}
> +
> +	tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
> +		     op->dummy.nbytes;
> +
> +	/*
> +	 * Allocate a buffer to transmit the CMD, ADDR cycles with
> kmalloc() so
> +	 * we're guaranteed that this buffer is DMA-able, as
> required by the
> +	 * SPI layer.
> +	 */
> +	tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
> +	if (!tmpbuf)
> +		return -ENOMEM;
> +
> +	spi_message_init(&msg);
> +
> +	tmpbuf[0] = op->cmd.opcode;
> +	xfers[xferpos].tx_buf = tmpbuf;
> +	xfers[xferpos].len = sizeof(op->cmd.opcode);
> +	xfers[xferpos].tx_nbits = op->cmd.buswidth;
> +	spi_message_add_tail(&xfers[xferpos], &msg);
> +	xferpos++;
> +	totalxferlen++;
> +
> +	if (op->addr.nbytes) {
> +		memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);
> +		xfers[xferpos].tx_buf = tmpbuf + 1;
> +		xfers[xferpos].len = op->addr.nbytes;
> +		xfers[xferpos].tx_nbits = op->addr.buswidth;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->addr.nbytes;
> +	}
> +
> +	if (op->dummy.nbytes) {
> +		memset(tmpbuf + op->addr.nbytes + 1, 0xff,
> op->dummy.nbytes);
> +		xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
> +		xfers[xferpos].len = op->dummy.nbytes;
> +		xfers[xferpos].tx_nbits = op->dummy.buswidth;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->dummy.nbytes;
> +	}

Can't we use just one xfer for all the opcode, addr and dummy bytes ?

> +	if (op->data.nbytes) {
> +		if (op->data.dir == SPI_MEM_DATA_IN) {
> +			xfers[xferpos].rx_buf = op->data.buf.in;
> +			xfers[xferpos].rx_nbits = op->data.buswidth;
> +		} else {
> +			xfers[xferpos].tx_buf = op->data.buf.out;
> +			xfers[xferpos].tx_nbits = op->data.buswidth;
> +		}
> +
> +		xfers[xferpos].len = op->data.nbytes;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->data.nbytes;
> +	}
> +
> +	ret = spi_sync(mem->spi, &msg);
> +
> +	kfree(tmpbuf);
> +
> +	if (ret)
> +		return ret;
> +
> +	if (msg.actual_length != totalxferlen)
> +		return -EIO;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_exec_op);

[...]

Thanks,

Maxime

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-02-06  9:43         ` Maxime Chevallier
  0 siblings, 0 replies; 92+ messages in thread
From: Maxime Chevallier @ 2018-02-06  9:43 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

Hi Boris,

> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> 
> Some controllers are exposing high-level interfaces to access various
> kind of SPI memories. Unfortunately they do not fit in the current
> spi_controller model and usually have drivers placed in
> drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> memories in general.
> 
> This is an attempt at defining a SPI memory interface which works for
> all kinds of SPI memories (NORs, NANDs, SRAMs).
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> ---
>  drivers/spi/spi.c       | 423
> +++++++++++++++++++++++++++++++++++++++++++++++-
> include/linux/spi/spi.h | 226 ++++++++++++++++++++++++++ 2 files
> changed, 646 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index b33a727a0158..57bc540a0521 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -2057,6 +2057,24 @@ static int of_spi_register_master(struct
> spi_controller *ctlr) }
>  #endif
>  

[...]

> +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> +	struct spi_controller *ctlr = mem->spi->controller;
> +	struct spi_transfer xfers[4] = { };
> +	struct spi_message msg;
> +	u8 *tmpbuf;
> +	int ret;
> +
> +	if (!spi_mem_supports_op(mem, op))
> +		return -ENOTSUPP;
> +
> +	if (ctlr->mem_ops) {
> +		if (ctlr->auto_runtime_pm) {
> +			ret = pm_runtime_get_sync(ctlr->dev.parent);
> +			if (ret < 0) {
> +				dev_err(&ctlr->dev,
> +					"Failed to power device:
> %d\n",
> +					ret);
> +				return ret;
> +			}
> +		}
> +
> +		mutex_lock(&ctlr->bus_lock_mutex);
> +		mutex_lock(&ctlr->io_mutex);
> +		ret = ctlr->mem_ops->exec_op(mem, op);

As a user, what prevented me from using spi_flash_read is that it
bypasses the message queue. I have a setup that uses spi_async and I
have to make sure everything goes in the right order, so I ended up
using spi_write_then_read instead.

Is there a way to make so that the message that uses exec_op are issued
in the correct order regarding messages that are already queued ?

Maybe we could extend spi_message or spi_transfer to store all
this opcode/dummy/addr information, so that we would use the standard
interfaces spi_sync / spi_async, and make this mechanism of exec_op
transparent from the user ?


> +		mutex_unlock(&ctlr->io_mutex);
> +		mutex_unlock(&ctlr->bus_lock_mutex);
> +
> +		if (ctlr->auto_runtime_pm)
> +			pm_runtime_put(ctlr->dev.parent);
> +
> +		/*
> +		 * Some controllers only optimize specific paths
> (typically the
> +		 * read path) and expect the core to use the regular
> SPI
> +		 * interface in these cases.
> +		 */
> +		if (!ret || ret != -ENOTSUPP)
> +			return ret;
> +	}
> +
> +	tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
> +		     op->dummy.nbytes;
> +
> +	/*
> +	 * Allocate a buffer to transmit the CMD, ADDR cycles with
> kmalloc() so
> +	 * we're guaranteed that this buffer is DMA-able, as
> required by the
> +	 * SPI layer.
> +	 */
> +	tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
> +	if (!tmpbuf)
> +		return -ENOMEM;
> +
> +	spi_message_init(&msg);
> +
> +	tmpbuf[0] = op->cmd.opcode;
> +	xfers[xferpos].tx_buf = tmpbuf;
> +	xfers[xferpos].len = sizeof(op->cmd.opcode);
> +	xfers[xferpos].tx_nbits = op->cmd.buswidth;
> +	spi_message_add_tail(&xfers[xferpos], &msg);
> +	xferpos++;
> +	totalxferlen++;
> +
> +	if (op->addr.nbytes) {
> +		memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);
> +		xfers[xferpos].tx_buf = tmpbuf + 1;
> +		xfers[xferpos].len = op->addr.nbytes;
> +		xfers[xferpos].tx_nbits = op->addr.buswidth;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->addr.nbytes;
> +	}
> +
> +	if (op->dummy.nbytes) {
> +		memset(tmpbuf + op->addr.nbytes + 1, 0xff,
> op->dummy.nbytes);
> +		xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
> +		xfers[xferpos].len = op->dummy.nbytes;
> +		xfers[xferpos].tx_nbits = op->dummy.buswidth;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->dummy.nbytes;
> +	}

Can't we use just one xfer for all the opcode, addr and dummy bytes ?

> +	if (op->data.nbytes) {
> +		if (op->data.dir == SPI_MEM_DATA_IN) {
> +			xfers[xferpos].rx_buf = op->data.buf.in;
> +			xfers[xferpos].rx_nbits = op->data.buswidth;
> +		} else {
> +			xfers[xferpos].tx_buf = op->data.buf.out;
> +			xfers[xferpos].tx_nbits = op->data.buswidth;
> +		}
> +
> +		xfers[xferpos].len = op->data.nbytes;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->data.nbytes;
> +	}
> +
> +	ret = spi_sync(mem->spi, &msg);
> +
> +	kfree(tmpbuf);
> +
> +	if (ret)
> +		return ret;
> +
> +	if (msg.actual_length != totalxferlen)
> +		return -EIO;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_exec_op);

[...]

Thanks,

Maxime

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-06  9:43         ` Maxime Chevallier
@ 2018-02-06 10:25           ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-06 10:25 UTC (permalink / raw)
  To: Maxime Chevallier
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Frieder Schrempf,
	Vignesh R, Yogesh Gaur, Rafał Miłecki, Kamal Dasu,
	Sourav Poddar

Hi Maxime,

On Tue, 6 Feb 2018 10:43:30 +0100
Maxime Chevallier <maxime.chevallier-fqqU8Ppg2Jk@public.gmane.org> wrote:

> Hi Boris,
> 
> > From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > 
> > Some controllers are exposing high-level interfaces to access various
> > kind of SPI memories. Unfortunately they do not fit in the current
> > spi_controller model and usually have drivers placed in
> > drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> > memories in general.
> > 
> > This is an attempt at defining a SPI memory interface which works for
> > all kinds of SPI memories (NORs, NANDs, SRAMs).
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > ---
> >  drivers/spi/spi.c       | 423
> > +++++++++++++++++++++++++++++++++++++++++++++++-
> > include/linux/spi/spi.h | 226 ++++++++++++++++++++++++++ 2 files
> > changed, 646 insertions(+), 3 deletions(-)
> > 
> > diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> > index b33a727a0158..57bc540a0521 100644
> > --- a/drivers/spi/spi.c
> > +++ b/drivers/spi/spi.c
> > @@ -2057,6 +2057,24 @@ static int of_spi_register_master(struct
> > spi_controller *ctlr) }
> >  #endif
> >    
> 
> [...]
> 
> > +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> > +{
> > +	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> > +	struct spi_controller *ctlr = mem->spi->controller;
> > +	struct spi_transfer xfers[4] = { };
> > +	struct spi_message msg;
> > +	u8 *tmpbuf;
> > +	int ret;
> > +
> > +	if (!spi_mem_supports_op(mem, op))
> > +		return -ENOTSUPP;
> > +
> > +	if (ctlr->mem_ops) {
> > +		if (ctlr->auto_runtime_pm) {
> > +			ret = pm_runtime_get_sync(ctlr->dev.parent);
> > +			if (ret < 0) {
> > +				dev_err(&ctlr->dev,
> > +					"Failed to power device:
> > %d\n",
> > +					ret);
> > +				return ret;
> > +			}
> > +		}
> > +
> > +		mutex_lock(&ctlr->bus_lock_mutex);
> > +		mutex_lock(&ctlr->io_mutex);
> > +		ret = ctlr->mem_ops->exec_op(mem, op);  
> 
> As a user, what prevented me from using spi_flash_read is that it
> bypasses the message queue. I have a setup that uses spi_async and I
> have to make sure everything goes in the right order, so I ended up
> using spi_write_then_read instead.
> 
> Is there a way to make so that the message that uses exec_op are issued
> in the correct order regarding messages that are already queued ?

Nope, that's not handled right now, and mem ops can indeed go in before
the already queued messages if spi_mem_exec_op() acquires the io_lock
before the dequeuing thread.

> 
> Maybe we could extend spi_message or spi_transfer to store all
> this opcode/dummy/addr information, so that we would use the standard
> interfaces spi_sync / spi_async, and make this mechanism of exec_op
> transparent from the user ?

That's a possibility. Note that SPI controllers are passed a spi_mem
object in ->exec_op(), not a spi_device. This is because I envision
we'll need to pass extra information to the controller to let it take
appropriate decisions (like the memory device size or other things that
may have an impact on how the controller handle the spi_mem_op
requests). 
Anyway, we could stuff a spi_mem_op and a spi_mem pointer in the
spi_message struct and adapt the queuing/dequeuing logic, but I fear
this will complexify the whole thing.

Another approach would be to flush the msg queue before calling
->exec_op(). That does not guarantee that the memory operation will be
placed exactly where it should (SPI messages might be queued while
we're pumping the queue), but I'm not sure we really care.

	/*
	 * When the generic queuing/dequeuing mechanism is in place
	 * pump all pending messages to enforce FIFO behavior, and
	 * execute the SPI mem operation after that.
	 */
	if (ctlr->transfer == spi_queued_transfer)
		__spi_pump_messages(ctlr, false);

	mutex_lock(&ctlr->bus_lock_mutex);
	mutex_lock(&ctlr->io_mutex);
	ret = ctlr->mem_ops->exec_op(mem, op);
	...	

> 
> 
> > +		mutex_unlock(&ctlr->io_mutex);
> > +		mutex_unlock(&ctlr->bus_lock_mutex);
> > +
> > +		if (ctlr->auto_runtime_pm)
> > +			pm_runtime_put(ctlr->dev.parent);
> > +
> > +		/*
> > +		 * Some controllers only optimize specific paths
> > (typically the
> > +		 * read path) and expect the core to use the regular
> > SPI
> > +		 * interface in these cases.
> > +		 */
> > +		if (!ret || ret != -ENOTSUPP)
> > +			return ret;
> > +	}
> > +
> > +	tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
> > +		     op->dummy.nbytes;
> > +
> > +	/*
> > +	 * Allocate a buffer to transmit the CMD, ADDR cycles with
> > kmalloc() so
> > +	 * we're guaranteed that this buffer is DMA-able, as
> > required by the
> > +	 * SPI layer.
> > +	 */
> > +	tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
> > +	if (!tmpbuf)
> > +		return -ENOMEM;
> > +
> > +	spi_message_init(&msg);
> > +
> > +	tmpbuf[0] = op->cmd.opcode;
> > +	xfers[xferpos].tx_buf = tmpbuf;
> > +	xfers[xferpos].len = sizeof(op->cmd.opcode);
> > +	xfers[xferpos].tx_nbits = op->cmd.buswidth;
> > +	spi_message_add_tail(&xfers[xferpos], &msg);
> > +	xferpos++;
> > +	totalxferlen++;
> > +
> > +	if (op->addr.nbytes) {
> > +		memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);
> > +		xfers[xferpos].tx_buf = tmpbuf + 1;
> > +		xfers[xferpos].len = op->addr.nbytes;
> > +		xfers[xferpos].tx_nbits = op->addr.buswidth;
> > +		spi_message_add_tail(&xfers[xferpos], &msg);
> > +		xferpos++;
> > +		totalxferlen += op->addr.nbytes;
> > +	}
> > +
> > +	if (op->dummy.nbytes) {
> > +		memset(tmpbuf + op->addr.nbytes + 1, 0xff,
> > op->dummy.nbytes);
> > +		xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
> > +		xfers[xferpos].len = op->dummy.nbytes;
> > +		xfers[xferpos].tx_nbits = op->dummy.buswidth;
> > +		spi_message_add_tail(&xfers[xferpos], &msg);
> > +		xferpos++;
> > +		totalxferlen += op->dummy.nbytes;
> > +	}  
> 
> Can't we use just one xfer for all the opcode, addr and dummy bytes ?

We can, if they have the same buswidth, but I didn't want to go that
far into optimization before making sure the approach was acceptable.
Definitely something I can add in a v2.

> 
> > +	if (op->data.nbytes) {
> > +		if (op->data.dir == SPI_MEM_DATA_IN) {
> > +			xfers[xferpos].rx_buf = op->data.buf.in;
> > +			xfers[xferpos].rx_nbits = op->data.buswidth;
> > +		} else {
> > +			xfers[xferpos].tx_buf = op->data.buf.out;
> > +			xfers[xferpos].tx_nbits = op->data.buswidth;
> > +		}
> > +
> > +		xfers[xferpos].len = op->data.nbytes;
> > +		spi_message_add_tail(&xfers[xferpos], &msg);
> > +		xferpos++;
> > +		totalxferlen += op->data.nbytes;
> > +	}
> > +
> > +	ret = spi_sync(mem->spi, &msg);
> > +
> > +	kfree(tmpbuf);
> > +
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (msg.actual_length != totalxferlen)
> > +		return -EIO;
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(spi_mem_exec_op);  
> 
> [...]
> 

Thanks for your feedback.

Boris

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-02-06 10:25           ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-06 10:25 UTC (permalink / raw)
  To: Maxime Chevallier
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

Hi Maxime,

On Tue, 6 Feb 2018 10:43:30 +0100
Maxime Chevallier <maxime.chevallier@smile.fr> wrote:

> Hi Boris,
> 
> > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> > 
> > Some controllers are exposing high-level interfaces to access various
> > kind of SPI memories. Unfortunately they do not fit in the current
> > spi_controller model and usually have drivers placed in
> > drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> > memories in general.
> > 
> > This is an attempt at defining a SPI memory interface which works for
> > all kinds of SPI memories (NORs, NANDs, SRAMs).
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > ---
> >  drivers/spi/spi.c       | 423
> > +++++++++++++++++++++++++++++++++++++++++++++++-
> > include/linux/spi/spi.h | 226 ++++++++++++++++++++++++++ 2 files
> > changed, 646 insertions(+), 3 deletions(-)
> > 
> > diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> > index b33a727a0158..57bc540a0521 100644
> > --- a/drivers/spi/spi.c
> > +++ b/drivers/spi/spi.c
> > @@ -2057,6 +2057,24 @@ static int of_spi_register_master(struct
> > spi_controller *ctlr) }
> >  #endif
> >    
> 
> [...]
> 
> > +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> > +{
> > +	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> > +	struct spi_controller *ctlr = mem->spi->controller;
> > +	struct spi_transfer xfers[4] = { };
> > +	struct spi_message msg;
> > +	u8 *tmpbuf;
> > +	int ret;
> > +
> > +	if (!spi_mem_supports_op(mem, op))
> > +		return -ENOTSUPP;
> > +
> > +	if (ctlr->mem_ops) {
> > +		if (ctlr->auto_runtime_pm) {
> > +			ret = pm_runtime_get_sync(ctlr->dev.parent);
> > +			if (ret < 0) {
> > +				dev_err(&ctlr->dev,
> > +					"Failed to power device:
> > %d\n",
> > +					ret);
> > +				return ret;
> > +			}
> > +		}
> > +
> > +		mutex_lock(&ctlr->bus_lock_mutex);
> > +		mutex_lock(&ctlr->io_mutex);
> > +		ret = ctlr->mem_ops->exec_op(mem, op);  
> 
> As a user, what prevented me from using spi_flash_read is that it
> bypasses the message queue. I have a setup that uses spi_async and I
> have to make sure everything goes in the right order, so I ended up
> using spi_write_then_read instead.
> 
> Is there a way to make so that the message that uses exec_op are issued
> in the correct order regarding messages that are already queued ?

Nope, that's not handled right now, and mem ops can indeed go in before
the already queued messages if spi_mem_exec_op() acquires the io_lock
before the dequeuing thread.

> 
> Maybe we could extend spi_message or spi_transfer to store all
> this opcode/dummy/addr information, so that we would use the standard
> interfaces spi_sync / spi_async, and make this mechanism of exec_op
> transparent from the user ?

That's a possibility. Note that SPI controllers are passed a spi_mem
object in ->exec_op(), not a spi_device. This is because I envision
we'll need to pass extra information to the controller to let it take
appropriate decisions (like the memory device size or other things that
may have an impact on how the controller handle the spi_mem_op
requests). 
Anyway, we could stuff a spi_mem_op and a spi_mem pointer in the
spi_message struct and adapt the queuing/dequeuing logic, but I fear
this will complexify the whole thing.

Another approach would be to flush the msg queue before calling
->exec_op(). That does not guarantee that the memory operation will be
placed exactly where it should (SPI messages might be queued while
we're pumping the queue), but I'm not sure we really care.

	/*
	 * When the generic queuing/dequeuing mechanism is in place
	 * pump all pending messages to enforce FIFO behavior, and
	 * execute the SPI mem operation after that.
	 */
	if (ctlr->transfer == spi_queued_transfer)
		__spi_pump_messages(ctlr, false);

	mutex_lock(&ctlr->bus_lock_mutex);
	mutex_lock(&ctlr->io_mutex);
	ret = ctlr->mem_ops->exec_op(mem, op);
	...	

> 
> 
> > +		mutex_unlock(&ctlr->io_mutex);
> > +		mutex_unlock(&ctlr->bus_lock_mutex);
> > +
> > +		if (ctlr->auto_runtime_pm)
> > +			pm_runtime_put(ctlr->dev.parent);
> > +
> > +		/*
> > +		 * Some controllers only optimize specific paths
> > (typically the
> > +		 * read path) and expect the core to use the regular
> > SPI
> > +		 * interface in these cases.
> > +		 */
> > +		if (!ret || ret != -ENOTSUPP)
> > +			return ret;
> > +	}
> > +
> > +	tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
> > +		     op->dummy.nbytes;
> > +
> > +	/*
> > +	 * Allocate a buffer to transmit the CMD, ADDR cycles with
> > kmalloc() so
> > +	 * we're guaranteed that this buffer is DMA-able, as
> > required by the
> > +	 * SPI layer.
> > +	 */
> > +	tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
> > +	if (!tmpbuf)
> > +		return -ENOMEM;
> > +
> > +	spi_message_init(&msg);
> > +
> > +	tmpbuf[0] = op->cmd.opcode;
> > +	xfers[xferpos].tx_buf = tmpbuf;
> > +	xfers[xferpos].len = sizeof(op->cmd.opcode);
> > +	xfers[xferpos].tx_nbits = op->cmd.buswidth;
> > +	spi_message_add_tail(&xfers[xferpos], &msg);
> > +	xferpos++;
> > +	totalxferlen++;
> > +
> > +	if (op->addr.nbytes) {
> > +		memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);
> > +		xfers[xferpos].tx_buf = tmpbuf + 1;
> > +		xfers[xferpos].len = op->addr.nbytes;
> > +		xfers[xferpos].tx_nbits = op->addr.buswidth;
> > +		spi_message_add_tail(&xfers[xferpos], &msg);
> > +		xferpos++;
> > +		totalxferlen += op->addr.nbytes;
> > +	}
> > +
> > +	if (op->dummy.nbytes) {
> > +		memset(tmpbuf + op->addr.nbytes + 1, 0xff,
> > op->dummy.nbytes);
> > +		xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
> > +		xfers[xferpos].len = op->dummy.nbytes;
> > +		xfers[xferpos].tx_nbits = op->dummy.buswidth;
> > +		spi_message_add_tail(&xfers[xferpos], &msg);
> > +		xferpos++;
> > +		totalxferlen += op->dummy.nbytes;
> > +	}  
> 
> Can't we use just one xfer for all the opcode, addr and dummy bytes ?

We can, if they have the same buswidth, but I didn't want to go that
far into optimization before making sure the approach was acceptable.
Definitely something I can add in a v2.

> 
> > +	if (op->data.nbytes) {
> > +		if (op->data.dir == SPI_MEM_DATA_IN) {
> > +			xfers[xferpos].rx_buf = op->data.buf.in;
> > +			xfers[xferpos].rx_nbits = op->data.buswidth;
> > +		} else {
> > +			xfers[xferpos].tx_buf = op->data.buf.out;
> > +			xfers[xferpos].tx_nbits = op->data.buswidth;
> > +		}
> > +
> > +		xfers[xferpos].len = op->data.nbytes;
> > +		spi_message_add_tail(&xfers[xferpos], &msg);
> > +		xferpos++;
> > +		totalxferlen += op->data.nbytes;
> > +	}
> > +
> > +	ret = spi_sync(mem->spi, &msg);
> > +
> > +	kfree(tmpbuf);
> > +
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (msg.actual_length != totalxferlen)
> > +		return -EIO;
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(spi_mem_exec_op);  
> 
> [...]
> 

Thanks for your feedback.

Boris

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-06  9:43         ` Maxime Chevallier
@ 2018-02-06 12:06           ` Mark Brown
  -1 siblings, 0 replies; 92+ messages in thread
From: Mark Brown @ 2018-02-06 12:06 UTC (permalink / raw)
  To: Maxime Chevallier
  Cc: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Frieder Schrempf,
	Vignesh R, Yogesh Gaur, Rafał Miłecki, Kamal Dasu,
	Sourav Poddar

[-- Attachment #1: Type: text/plain, Size: 991 bytes --]

On Tue, Feb 06, 2018 at 10:43:30AM +0100, Maxime Chevallier wrote:

> As a user, what prevented me from using spi_flash_read is that it
> bypasses the message queue. I have a setup that uses spi_async and I
> have to make sure everything goes in the right order, so I ended up
> using spi_write_then_read instead.

> Is there a way to make so that the message that uses exec_op are issued
> in the correct order regarding messages that are already queued ?

> Maybe we could extend spi_message or spi_transfer to store all
> this opcode/dummy/addr information, so that we would use the standard
> interfaces spi_sync / spi_async, and make this mechanism of exec_op
> transparent from the user ?

Or have the flash read flush out the queue perhaps, might be simpler.
Pushing the memory mapped read operations through the queue does seem to
defeat some of the purpose of having them though I can see it might be
needed, I think the current applications were read only or at least read
mostly.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-02-06 12:06           ` Mark Brown
  0 siblings, 0 replies; 92+ messages in thread
From: Mark Brown @ 2018-02-06 12:06 UTC (permalink / raw)
  To: Maxime Chevallier
  Cc: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen, linux-mtd,
	linux-spi, Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

[-- Attachment #1: Type: text/plain, Size: 991 bytes --]

On Tue, Feb 06, 2018 at 10:43:30AM +0100, Maxime Chevallier wrote:

> As a user, what prevented me from using spi_flash_read is that it
> bypasses the message queue. I have a setup that uses spi_async and I
> have to make sure everything goes in the right order, so I ended up
> using spi_write_then_read instead.

> Is there a way to make so that the message that uses exec_op are issued
> in the correct order regarding messages that are already queued ?

> Maybe we could extend spi_message or spi_transfer to store all
> this opcode/dummy/addr information, so that we would use the standard
> interfaces spi_sync / spi_async, and make this mechanism of exec_op
> transparent from the user ?

Or have the flash read flush out the queue perhaps, might be simpler.
Pushing the memory mapped read operations through the queue does seem to
defeat some of the purpose of having them though I can see it might be
needed, I think the current applications were read only or at least read
mostly.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-05 23:21     ` Boris Brezillon
@ 2018-02-09 12:52         ` Miquel Raynal
  -1 siblings, 0 replies; 92+ messages in thread
From: Miquel Raynal @ 2018-02-09 12:52 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Yogesh Gaur, Vignesh R,
	Kamal Dasu, Peter Pan, Frieder Schrempf, Rafał Miłecki,
	Sourav Poddar

Hi Boris,

Just a few comments on the form below.

On Tue,  6 Feb 2018 00:21:15 +0100, Boris Brezillon
<boris.brezillon-LDxbnhwyfcJBDgjK7y7TUQ@public.gmane.org> wrote:

> From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> 
> Some controllers are exposing high-level interfaces to access various
> kind of SPI memories. Unfortunately they do not fit in the current
> spi_controller model and usually have drivers placed in
> drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> memories in general.
> 
> This is an attempt at defining a SPI memory interface which works for
> all kinds of SPI memories (NORs, NANDs, SRAMs).
> 
> Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> ---
>  drivers/spi/spi.c       | 423 +++++++++++++++++++++++++++++++++++++++++++++++-
>  include/linux/spi/spi.h | 226 ++++++++++++++++++++++++++
>  2 files changed, 646 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index b33a727a0158..57bc540a0521 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -2057,6 +2057,24 @@ static int of_spi_register_master(struct spi_controller *ctlr)
>  }
>  #endif
>  
> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> +{
> +	/*
> +	 * The controller can implement only the high-level SPI-memory like
> +	 * operations if it does not support regular SPI transfers.
> +	 */
> +	if (ctlr->mem_ops) {
> +		if (!ctlr->mem_ops->supports_op ||
> +		    !ctlr->mem_ops->exec_op)
> +			return -EINVAL;
> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> +		   !ctlr->transfer_one_message) {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  /**
>   * spi_register_controller - register SPI master or slave controller
>   * @ctlr: initialized master, originally from spi_alloc_master() or
> @@ -2090,6 +2108,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>  	if (!dev)
>  		return -ENODEV;
>  
> +	/*
> +	 * Make sure all necessary hooks are implemented before registering
> +	 * the SPI controller.
> +	 */
> +	status = spi_controller_check_ops(ctlr);
> +	if (status)
> +		return status;
> +
>  	if (!spi_controller_is_slave(ctlr)) {
>  		status = of_spi_register_master(ctlr);
>  		if (status)
> @@ -2155,10 +2181,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>  			spi_controller_is_slave(ctlr) ? "slave" : "master",
>  			dev_name(&ctlr->dev));
>  
> -	/* If we're using a queued driver, start the queue */
> -	if (ctlr->transfer)
> +	/*
> +	 * If we're using a queued driver, start the queue. Note that we don't
> +	 * need the queueing logic if the driver is only supporting high-level
> +	 * memory operations.
> +	 */
> +	if (ctlr->transfer) {
>  		dev_info(dev, "controller is unqueued, this is deprecated\n");
> -	else {
> +	} else if (ctlr->transfer_one || ctlr->transfer_one_message) {
>  		status = spi_controller_initialize_queue(ctlr);
>  		if (status) {
>  			device_del(&ctlr->dev);
> @@ -2893,6 +2923,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
>  {
>  	struct spi_controller *ctlr = spi->controller;
>  
> +	/*
> +	 * Some controllers do not support doing regular SPI transfers. Return
> +	 * ENOTSUPP when this is the case.
> +	 */
> +	if (!ctlr->transfer)
> +		return -ENOTSUPP;
> +
>  	message->spi = spi;
>  
>  	SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_async);
> @@ -3321,6 +3358,386 @@ int spi_write_then_read(struct spi_device *spi,
>  }
>  EXPORT_SYMBOL_GPL(spi_write_then_read);
>  
> +/**
> + * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a
> + *					  memory operation
> + * @ctlr: the SPI controller requesting this dma_map()
> + * @op: the memory operation containing the buffer to map
> + * @sgt: a pointer to a non-initialized sg_table that will be filled by this
> + *	 function
> + *
> + * Some controllers might want to do DMA on the data buffer embedded in @op.
> + * This helper prepares everything for you and provides a ready-to-use
> + * sg_table. This function is not intended to be called from spi drivers.
> + * Only SPI controller drivers should use it.
> + * Note that the caller must ensure the memory region pointed by
> + * op->data.buf.{in,out} is DMA-able before calling this function.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
> +				       const struct spi_mem_op *op,
> +				       struct sg_table *sgt)
> +{
> +	struct device *dmadev;
> +
> +	if (!op->data.nbytes)
> +		return -EINVAL;
> +
> +	if (op->data.dir == SPI_MEM_DATA_OUT)
> +		dmadev = ctlr->dma_tx ?
> +			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +	else
> +		dmadev = ctlr->dma_rx ?
> +			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
> +
> +	if (!dmadev)
> +		return -EINVAL;
> +
> +	return spi_map_buf(ctlr, dmadev, sgt, op->data.buf.in, op->data.nbytes,
> +			   op->data.dir == SPI_MEM_DATA_IN ?
> +			   DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL_GPL(spi_controller_dma_map_mem_op_data);
> +
> +/**
> + * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a
> + *					    memory operation
> + * @ctlr: the SPI controller requesting this dma_unmap()
> + * @op: the memory operation containing the buffer to unmap
> + * @sgt: a pointer to an sg_table previously initialized by
> + *	 spi_controller_dma_map_mem_op_data()
> + *
> + * Some controllers might want to do DMA on the data buffer embedded in @op.
> + * This helper prepares things so that the CPU can access the
> + * op->data.buf.{in,out} buffer again.
> + *
> + * This function is not intended to be called from spi drivers. Only SPI

s/spi/SPI/

> + * controller drivers should use it.
> + *
> + * This function should be called after the DMA operation has finished an is

s/an/and/

> + * only valid if the previous spi_controller_dma_map_mem_op_data() has returned
> + * 0.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> +					  const struct spi_mem_op *op,
> +					  struct sg_table *sgt)
> +{
> +	struct device *dmadev;
> +
> +	if (!op->data.nbytes)
> +		return;
> +
> +	if (op->data.dir == SPI_MEM_DATA_OUT)
> +		dmadev = ctlr->dma_tx ?
> +			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +	else
> +		dmadev = ctlr->dma_rx ?
> +			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
> +
> +	spi_unmap_buf(ctlr, dmadev, sgt,
> +		      op->data.dir == SPI_MEM_DATA_IN ?
> +		      DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data);
> +
> +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
> +{
> +	u32 mode = mem->spi->mode;
> +
> +	switch (buswidth) {
> +	case 1:
> +		return 0;
> +
> +	case 2:
> +		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> +		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
> +			return 0;
> +
> +		break;
> +
> +	case 4:
> +		if ((tx && (mode & SPI_TX_QUAD)) ||
> +		    (!tx && (mode & SPI_RX_QUAD)))
> +			return 0;
> +
> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	return -ENOTSUPP;
> +}
> +
> +/**
> + * spi_mem_supports_op() - Check if a memory device and the controller it is
> + *			   connected to support a specific memory operation
> + * @mem: the SPI memory
> + * @op: the memory operation to check
> + *
> + * Some controllers are only supporting Single or Dual IOs, others might only
> + * support specific opcodes, or it can even be that the controller and device
> + * both support Quad IOs but the hardware prevents you from using it because
> + * only 2 IO lines are connected.
> + *
> + * This function checks whether a specific operation is supported.
> + *
> + * Return: true if @op is supported, false otherwise.
> + */
> +bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +	struct spi_controller *ctlr = mem->spi->controller;
> +
> +	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
> +		return false;
> +
> +	if (op->addr.nbytes &&
> +	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
> +		return false;
> +
> +	if (op->dummy.nbytes &&
> +	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
> +		return false;
> +
> +	if (op->data.nbytes &&
> +	    spi_check_buswidth_req(mem, op->data.buswidth,
> +				   op->data.dir == SPI_MEM_DATA_IN ?
> +				   false : true))

Why not just op->data.dir != SPI_MEM_DATA_IN or even better ==
SPI_MEM_DATA_OUT if it exists?

> +		return false;
> +
> +	if (ctlr->mem_ops)
> +		return ctlr->mem_ops->supports_op(mem, op);
> +
> +	return true;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_supports_op);
> +
> +/**
> + * spi_mem_exec_op() - Execute a memory operation
> + * @mem: the SPI memory
> + * @op: the memory operation to execute
> + *
> + * Executes a memory operation.
> + *
> + * This function first checks that @op is supported and then tries to execute
> + * it.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> +	struct spi_controller *ctlr = mem->spi->controller;
> +	struct spi_transfer xfers[4] = { };
> +	struct spi_message msg;
> +	u8 *tmpbuf;
> +	int ret;
> +
> +	if (!spi_mem_supports_op(mem, op))
> +		return -ENOTSUPP;
> +
> +	if (ctlr->mem_ops) {
> +		if (ctlr->auto_runtime_pm) {
> +			ret = pm_runtime_get_sync(ctlr->dev.parent);
> +			if (ret < 0) {
> +				dev_err(&ctlr->dev,
> +					"Failed to power device: %d\n",
> +					ret);
> +				return ret;
> +			}
> +		}
> +
> +		mutex_lock(&ctlr->bus_lock_mutex);
> +		mutex_lock(&ctlr->io_mutex);
> +		ret = ctlr->mem_ops->exec_op(mem, op);
> +		mutex_unlock(&ctlr->io_mutex);
> +		mutex_unlock(&ctlr->bus_lock_mutex);

Is not this a bit dangerous? I guess that no one should release the bus
lock without having already released the IO lock but maybe this should
be clearly mentioned in a comment in the original structure definition?

> +
> +		if (ctlr->auto_runtime_pm)
> +			pm_runtime_put(ctlr->dev.parent);
> +
> +		/*
> +		 * Some controllers only optimize specific paths (typically the
> +		 * read path) and expect the core to use the regular SPI
> +		 * interface in these cases.
> +		 */
> +		if (!ret || ret != -ENOTSUPP)
> +			return ret;
> +	}
> +
> +	tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
> +		     op->dummy.nbytes;
> +
> +	/*
> +	 * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
> +	 * we're guaranteed that this buffer is DMA-able, as required by the
> +	 * SPI layer.
> +	 */
> +	tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
> +	if (!tmpbuf)
> +		return -ENOMEM;
> +
> +	spi_message_init(&msg);
> +
> +	tmpbuf[0] = op->cmd.opcode;
> +	xfers[xferpos].tx_buf = tmpbuf;
> +	xfers[xferpos].len = sizeof(op->cmd.opcode);
> +	xfers[xferpos].tx_nbits = op->cmd.buswidth;
> +	spi_message_add_tail(&xfers[xferpos], &msg);
> +	xferpos++;
> +	totalxferlen++;
> +
> +	if (op->addr.nbytes) {
> +		memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);
> +		xfers[xferpos].tx_buf = tmpbuf + 1;
> +		xfers[xferpos].len = op->addr.nbytes;
> +		xfers[xferpos].tx_nbits = op->addr.buswidth;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->addr.nbytes;
> +	}
> +
> +	if (op->dummy.nbytes) {
> +		memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes);
> +		xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
> +		xfers[xferpos].len = op->dummy.nbytes;
> +		xfers[xferpos].tx_nbits = op->dummy.buswidth;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->dummy.nbytes;
> +	}
> +
> +	if (op->data.nbytes) {
> +		if (op->data.dir == SPI_MEM_DATA_IN) {
> +			xfers[xferpos].rx_buf = op->data.buf.in;
> +			xfers[xferpos].rx_nbits = op->data.buswidth;
> +		} else {
> +			xfers[xferpos].tx_buf = op->data.buf.out;
> +			xfers[xferpos].tx_nbits = op->data.buswidth;
> +		}
> +
> +		xfers[xferpos].len = op->data.nbytes;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->data.nbytes;
> +	}
> +
> +	ret = spi_sync(mem->spi, &msg);
> +
> +	kfree(tmpbuf);
> +
> +	if (ret)
> +		return ret;
> +
> +	if (msg.actual_length != totalxferlen)
> +		return -EIO;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_exec_op);
> +
> +/**
> + * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
> + *			      match controller limitations
> + * @mem: the SPI memory
> + * @op: the operation to adjust
> + *
> + * Some controllers have FIFO limitations and must split a data transfer
> + * operation into multiple ones, others require a specific alignment for
> + * optimized accesses. This function allows SPI mem drivers to split a single
> + * operation into multiple sub-operations when required.
> + *
> + * Return: a negative error code if the controller can't properly adjust @op,
> + *	   0 otherwise. Note that @op->data.nbytes will be updated if @op
> + *	   can't be handled in a single step.
> + */
> +int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
> +{
> +	struct spi_controller *ctlr = mem->spi->controller;
> +
> +	if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
> +		return ctlr->mem_ops->adjust_op_size(mem, op);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
> +
> +static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
> +{
> +	return container_of(drv, struct spi_mem_driver, spidrv.driver);
> +}
> +
> +static int spi_mem_probe(struct spi_device *spi)
> +{
> +	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +	struct spi_mem *mem;
> +
> +	mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL);
> +	if (!mem)
> +		return -ENOMEM;
> +
> +	mem->spi = spi;
> +	spi_set_drvdata(spi, mem);
> +
> +	return memdrv->probe(mem);
> +}
> +
> +static int spi_mem_remove(struct spi_device *spi)
> +{
> +	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +	struct spi_mem *mem = spi_get_drvdata(spi);
> +
> +	if (memdrv->remove)
> +		return memdrv->remove(mem);
> +
> +	return 0;
> +}
> +
> +static void spi_mem_shutdown(struct spi_device *spi)
> +{
> +	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +	struct spi_mem *mem = spi_get_drvdata(spi);
> +
> +	if (memdrv->shutdown)
> +		memdrv->shutdown(mem);
> +}
> +
> +/**
> + * spi_mem_driver_register_with_owner() - Register a SPI memory driver
> + * @memdrv: the SPI memory driver to register
> + * @owner: the owner of this driver
> + *
> + * Registers a SPI memory driver.
> + *
> + * Return: 0 in case of success, a negative error core otherwise.
> + */
> +
> +int spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv,
> +				       struct module *owner)
> +{
> +	memdrv->spidrv.probe = spi_mem_probe;
> +	memdrv->spidrv.remove = spi_mem_remove;
> +	memdrv->spidrv.shutdown = spi_mem_shutdown;
> +
> +	return __spi_register_driver(owner, &memdrv->spidrv);
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner);
> +
> +/**
> + * spi_mem_driver_unregister_with_owner() - Unregister a SPI memory driver
> + * @memdrv: the SPI memory driver to unregister
> + *
> + * Unregisters a SPI memory driver.
> + */
> +void spi_mem_driver_unregister(struct spi_mem_driver *memdrv)
> +{
> +	spi_unregister_driver(&memdrv->spidrv);
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_driver_unregister);
> +
>  /*-------------------------------------------------------------------------*/
>  
>  #if IS_ENABLED(CONFIG_OF_DYNAMIC)
> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> index 7b2170bfd6e7..af3c4ac62b55 100644
> --- a/include/linux/spi/spi.h
> +++ b/include/linux/spi/spi.h
> @@ -27,6 +27,7 @@ struct property_entry;
>  struct spi_controller;
>  struct spi_transfer;
>  struct spi_flash_read_message;
> +struct spi_controller_mem_ops;
>  
>  /*
>   * INTERFACES between SPI master-side drivers and SPI slave protocol handlers,
> @@ -376,6 +377,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
>   *                    transfer_one callback.
>   * @handle_err: the subsystem calls the driver to handle an error that occurs
>   *		in the generic implementation of transfer_one_message().
> + * @mem_ops: optimized/dedicated operations for interactions with SPI memory.
> + *	     This field is optional and should only be implemented if the
> + *	     controller has native support for memory like operations.
>   * @unprepare_message: undo any work done by prepare_message().
>   * @slave_abort: abort the ongoing transfer request on an SPI slave controller
>   * @spi_flash_read: to support spi-controller hardwares that provide
> @@ -564,6 +568,9 @@ struct spi_controller {
>  	void (*handle_err)(struct spi_controller *ctlr,
>  			   struct spi_message *message);
>  
> +	/* Optimized handlers for SPI memory-like operations. */
> +	const struct spi_controller_mem_ops *mem_ops;
> +
>  	/* gpio chip select */
>  	int			*cs_gpios;
>  
> @@ -1227,6 +1234,225 @@ int spi_flash_read(struct spi_device *spi,
>  
>  /*---------------------------------------------------------------------------*/
>  
> +/* SPI memory related definitions. */
> +
> +#define SPI_MEM_OP_CMD(__opcode, __buswidth)			\
> +	{							\
> +		.buswidth = __buswidth,				\
> +		.opcode = __opcode,				\
> +	}
> +
> +#define SPI_MEM_OP_ADDRS(__nbytes, __buf, __buswidth)		\
> +	{							\
> +		.nbytes = __nbytes,				\
> +		.buf = __buf,					\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_NO_ADDRS	{ }
> +
> +#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth)			\
> +	{							\
> +		.nbytes = __nbytes,				\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_NO_DUMMY	{ }
> +
> +#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth)		\
> +	{							\
> +		.dir = SPI_MEM_DATA_IN,				\
> +		.nbytes = __nbytes,				\
> +		.buf.in = __buf,				\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth)	\
> +	{							\
> +		.dir = SPI_MEM_DATA_OUT,			\
> +		.nbytes = __nbytes,				\
> +		.buf.out = __buf,				\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_NO_DATA	{ }
> +
> +/**
> + * enum spi_mem_data_dir - describes the direction of a SPI memory data
> + *			   transfer from the controller perspective
> + * @SPI_MEM_DATA_IN: data coming from the SPI memory
> + * @SPI_MEM_DATA_OUT: data sent the SPI memory
> + */
> +enum spi_mem_data_dir {
> +	SPI_MEM_DATA_IN,
> +	SPI_MEM_DATA_OUT,
> +};
> +
> +/**
> + * struct spi_mem_op - describes a SPI memory operation
> + * @cmd.buswidth: number of IO lines used to transmit the command
> + * @cmd.opcode: operation opcode
> + * @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.buf: buffer storing the address bytes
> + * @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
> + * @data.buswidth: number of IO lanes used to send/receive the data
> + * @data.dir: direction of the transfer
> + * @data.buf.in: input buffer
> + * @data.buf.out: output buffer
> + */
> +struct spi_mem_op {
> +	struct {
> +		u8 buswidth;
> +		u8 opcode;
> +	} cmd;
> +
> +	struct {
> +		u8 nbytes;
> +		u8 buswidth;
> +		const u8 *buf;
> +	} addr;
> +
> +	struct {
> +		u8 nbytes;
> +		u8 buswidth;
> +	} dummy;
> +
> +	struct {
> +		u8 buswidth;
> +		enum spi_mem_data_dir dir;
> +		unsigned int nbytes;
> +		/* buf.{in,out} must be DMA-able. */
> +		union {
> +			void *in;
> +			const void *out;
> +		} buf;
> +	} data;
> +};
> +
> +#define SPI_MEM_OP(__cmd, __addr, __dummy, __data)		\
> +	{							\
> +		.cmd = __cmd,					\
> +		.addr = __addr,					\
> +		.dummy = __dummy,				\
> +		.data = __data,					\
> +	}
> +
> +/**
> + * struct spi_mem - describes a SPI memory device
> + * @spi: the underlying SPI device
> + * @drvpriv: spi_mem_drviver private data
> + *
> + * Extra information that describe the SPI memory device and may be needed by
> + * the controller to properly handle this device should be placed here.
> + *
> + * One example would be the device size since some controller expose their SPI
> + * mem devices through a io-mapped region.
> + */
> +struct spi_mem {
> +	struct spi_device *spi;
> +	void *drvpriv;
> +};
> +
> +/**
> + * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
> + *				  device
> + * @mem: memory device
> + * @data: data to attach to the memory device
> + */
> +static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
> +{
> +	mem->drvpriv = data;
> +}
> +
> +/**
> + * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
> + *				  device
> + * @mem: memory device
> + *
> + * Return: the data attached to the mem device.
> + */
> +static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
> +{
> +	return mem->drvpriv;
> +}
> +
> +/**
> + * 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)
> + * @supports_op: check if an operation is supported by the controller
> + * @exec_op: execute a SPI memory operation
> + *
> + * 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.
> + */
> +struct spi_controller_mem_ops {
> +	int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
> +	bool (*supports_op)(struct spi_mem *mem,
> +			    const struct spi_mem_op *op);
> +	int (*exec_op)(struct spi_mem *mem,
> +		       const struct spi_mem_op *op);

:)

> +};
> +
> +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
> +				       const struct spi_mem_op *op,
> +				       struct sg_table *sg);
> +
> +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> +					  const struct spi_mem_op *op,
> +					  struct sg_table *sg);
> +
> +int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
> +
> +bool spi_mem_supports_op(struct spi_mem *mem,
> +			 const struct spi_mem_op *op);
> +
> +int spi_mem_exec_op(struct spi_mem *mem,
> +		    const struct spi_mem_op *op);
> +
> +/**
> + * struct spi_mem_driver - SPI memory driver
> + * @spidrv: inherit from a SPI driver
> + * @probe: probe a SPI memory. Usually where detection/initialization takes
> + *	   place
> + * @remove: remove a SPI memory
> + * @shutdown: take appropriate action when the system is shutdown
> + *
> + * This is just a thin wrapper around a spi_driver. The core takes care of
> + * allocating the spi_mem object and forwarding the probe/remove/shutdown
> + * request to the spi_mem_driver. The reason we use this wrapper is because
> + * we might have to stuff more information into the spi_mem struct to let
> + * SPI controllers know more about the SPI memory they interact with, and
> + * having this intermediate layer allows us to do that without adding more
> + * useless fields to the spi_device object.
> + */
> +struct spi_mem_driver {
> +	struct spi_driver spidrv;
> +	int (*probe)(struct spi_mem *mem);
> +	int (*remove)(struct spi_mem *mem);
> +	void (*shutdown)(struct spi_mem *mem);
> +};
> +
> +int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
> +				       struct module *owner);
> +
> +#define spi_mem_driver_register(__drv)                                  \
> +	spi_mem_driver_register_with_owner(__drv, THIS_MODULE)
> +
> +void spi_mem_driver_unregister(struct spi_mem_driver *drv);
> +
> +#define module_spi_mem_driver(__drv)                                    \
> +	module_driver(__drv, spi_mem_driver_register,                   \
> +		      spi_mem_driver_unregister)
> +
> +/*---------------------------------------------------------------------------*/
> +
>  /*
>   * INTERFACE between board init code and SPI infrastructure.
>   *


Best regards,
Miquèl

-- 
Miquel Raynal, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-02-09 12:52         ` Miquel Raynal
  0 siblings, 0 replies; 92+ messages in thread
From: Miquel Raynal @ 2018-02-09 12:52 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Yogesh Gaur, Vignesh R, Kamal Dasu, Peter Pan,
	Frieder Schrempf, Rafał Miłecki, Sourav Poddar

Hi Boris,

Just a few comments on the form below.

On Tue,  6 Feb 2018 00:21:15 +0100, Boris Brezillon
<boris.brezillon@bootlin.com> wrote:

> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> 
> Some controllers are exposing high-level interfaces to access various
> kind of SPI memories. Unfortunately they do not fit in the current
> spi_controller model and usually have drivers placed in
> drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> memories in general.
> 
> This is an attempt at defining a SPI memory interface which works for
> all kinds of SPI memories (NORs, NANDs, SRAMs).
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> ---
>  drivers/spi/spi.c       | 423 +++++++++++++++++++++++++++++++++++++++++++++++-
>  include/linux/spi/spi.h | 226 ++++++++++++++++++++++++++
>  2 files changed, 646 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index b33a727a0158..57bc540a0521 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -2057,6 +2057,24 @@ static int of_spi_register_master(struct spi_controller *ctlr)
>  }
>  #endif
>  
> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> +{
> +	/*
> +	 * The controller can implement only the high-level SPI-memory like
> +	 * operations if it does not support regular SPI transfers.
> +	 */
> +	if (ctlr->mem_ops) {
> +		if (!ctlr->mem_ops->supports_op ||
> +		    !ctlr->mem_ops->exec_op)
> +			return -EINVAL;
> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> +		   !ctlr->transfer_one_message) {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  /**
>   * spi_register_controller - register SPI master or slave controller
>   * @ctlr: initialized master, originally from spi_alloc_master() or
> @@ -2090,6 +2108,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>  	if (!dev)
>  		return -ENODEV;
>  
> +	/*
> +	 * Make sure all necessary hooks are implemented before registering
> +	 * the SPI controller.
> +	 */
> +	status = spi_controller_check_ops(ctlr);
> +	if (status)
> +		return status;
> +
>  	if (!spi_controller_is_slave(ctlr)) {
>  		status = of_spi_register_master(ctlr);
>  		if (status)
> @@ -2155,10 +2181,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>  			spi_controller_is_slave(ctlr) ? "slave" : "master",
>  			dev_name(&ctlr->dev));
>  
> -	/* If we're using a queued driver, start the queue */
> -	if (ctlr->transfer)
> +	/*
> +	 * If we're using a queued driver, start the queue. Note that we don't
> +	 * need the queueing logic if the driver is only supporting high-level
> +	 * memory operations.
> +	 */
> +	if (ctlr->transfer) {
>  		dev_info(dev, "controller is unqueued, this is deprecated\n");
> -	else {
> +	} else if (ctlr->transfer_one || ctlr->transfer_one_message) {
>  		status = spi_controller_initialize_queue(ctlr);
>  		if (status) {
>  			device_del(&ctlr->dev);
> @@ -2893,6 +2923,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
>  {
>  	struct spi_controller *ctlr = spi->controller;
>  
> +	/*
> +	 * Some controllers do not support doing regular SPI transfers. Return
> +	 * ENOTSUPP when this is the case.
> +	 */
> +	if (!ctlr->transfer)
> +		return -ENOTSUPP;
> +
>  	message->spi = spi;
>  
>  	SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_async);
> @@ -3321,6 +3358,386 @@ int spi_write_then_read(struct spi_device *spi,
>  }
>  EXPORT_SYMBOL_GPL(spi_write_then_read);
>  
> +/**
> + * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a
> + *					  memory operation
> + * @ctlr: the SPI controller requesting this dma_map()
> + * @op: the memory operation containing the buffer to map
> + * @sgt: a pointer to a non-initialized sg_table that will be filled by this
> + *	 function
> + *
> + * Some controllers might want to do DMA on the data buffer embedded in @op.
> + * This helper prepares everything for you and provides a ready-to-use
> + * sg_table. This function is not intended to be called from spi drivers.
> + * Only SPI controller drivers should use it.
> + * Note that the caller must ensure the memory region pointed by
> + * op->data.buf.{in,out} is DMA-able before calling this function.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
> +				       const struct spi_mem_op *op,
> +				       struct sg_table *sgt)
> +{
> +	struct device *dmadev;
> +
> +	if (!op->data.nbytes)
> +		return -EINVAL;
> +
> +	if (op->data.dir == SPI_MEM_DATA_OUT)
> +		dmadev = ctlr->dma_tx ?
> +			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +	else
> +		dmadev = ctlr->dma_rx ?
> +			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
> +
> +	if (!dmadev)
> +		return -EINVAL;
> +
> +	return spi_map_buf(ctlr, dmadev, sgt, op->data.buf.in, op->data.nbytes,
> +			   op->data.dir == SPI_MEM_DATA_IN ?
> +			   DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL_GPL(spi_controller_dma_map_mem_op_data);
> +
> +/**
> + * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a
> + *					    memory operation
> + * @ctlr: the SPI controller requesting this dma_unmap()
> + * @op: the memory operation containing the buffer to unmap
> + * @sgt: a pointer to an sg_table previously initialized by
> + *	 spi_controller_dma_map_mem_op_data()
> + *
> + * Some controllers might want to do DMA on the data buffer embedded in @op.
> + * This helper prepares things so that the CPU can access the
> + * op->data.buf.{in,out} buffer again.
> + *
> + * This function is not intended to be called from spi drivers. Only SPI

s/spi/SPI/

> + * controller drivers should use it.
> + *
> + * This function should be called after the DMA operation has finished an is

s/an/and/

> + * only valid if the previous spi_controller_dma_map_mem_op_data() has returned
> + * 0.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> +					  const struct spi_mem_op *op,
> +					  struct sg_table *sgt)
> +{
> +	struct device *dmadev;
> +
> +	if (!op->data.nbytes)
> +		return;
> +
> +	if (op->data.dir == SPI_MEM_DATA_OUT)
> +		dmadev = ctlr->dma_tx ?
> +			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +	else
> +		dmadev = ctlr->dma_rx ?
> +			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
> +
> +	spi_unmap_buf(ctlr, dmadev, sgt,
> +		      op->data.dir == SPI_MEM_DATA_IN ?
> +		      DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data);
> +
> +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
> +{
> +	u32 mode = mem->spi->mode;
> +
> +	switch (buswidth) {
> +	case 1:
> +		return 0;
> +
> +	case 2:
> +		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> +		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
> +			return 0;
> +
> +		break;
> +
> +	case 4:
> +		if ((tx && (mode & SPI_TX_QUAD)) ||
> +		    (!tx && (mode & SPI_RX_QUAD)))
> +			return 0;
> +
> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	return -ENOTSUPP;
> +}
> +
> +/**
> + * spi_mem_supports_op() - Check if a memory device and the controller it is
> + *			   connected to support a specific memory operation
> + * @mem: the SPI memory
> + * @op: the memory operation to check
> + *
> + * Some controllers are only supporting Single or Dual IOs, others might only
> + * support specific opcodes, or it can even be that the controller and device
> + * both support Quad IOs but the hardware prevents you from using it because
> + * only 2 IO lines are connected.
> + *
> + * This function checks whether a specific operation is supported.
> + *
> + * Return: true if @op is supported, false otherwise.
> + */
> +bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +	struct spi_controller *ctlr = mem->spi->controller;
> +
> +	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
> +		return false;
> +
> +	if (op->addr.nbytes &&
> +	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
> +		return false;
> +
> +	if (op->dummy.nbytes &&
> +	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
> +		return false;
> +
> +	if (op->data.nbytes &&
> +	    spi_check_buswidth_req(mem, op->data.buswidth,
> +				   op->data.dir == SPI_MEM_DATA_IN ?
> +				   false : true))

Why not just op->data.dir != SPI_MEM_DATA_IN or even better ==
SPI_MEM_DATA_OUT if it exists?

> +		return false;
> +
> +	if (ctlr->mem_ops)
> +		return ctlr->mem_ops->supports_op(mem, op);
> +
> +	return true;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_supports_op);
> +
> +/**
> + * spi_mem_exec_op() - Execute a memory operation
> + * @mem: the SPI memory
> + * @op: the memory operation to execute
> + *
> + * Executes a memory operation.
> + *
> + * This function first checks that @op is supported and then tries to execute
> + * it.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> +	struct spi_controller *ctlr = mem->spi->controller;
> +	struct spi_transfer xfers[4] = { };
> +	struct spi_message msg;
> +	u8 *tmpbuf;
> +	int ret;
> +
> +	if (!spi_mem_supports_op(mem, op))
> +		return -ENOTSUPP;
> +
> +	if (ctlr->mem_ops) {
> +		if (ctlr->auto_runtime_pm) {
> +			ret = pm_runtime_get_sync(ctlr->dev.parent);
> +			if (ret < 0) {
> +				dev_err(&ctlr->dev,
> +					"Failed to power device: %d\n",
> +					ret);
> +				return ret;
> +			}
> +		}
> +
> +		mutex_lock(&ctlr->bus_lock_mutex);
> +		mutex_lock(&ctlr->io_mutex);
> +		ret = ctlr->mem_ops->exec_op(mem, op);
> +		mutex_unlock(&ctlr->io_mutex);
> +		mutex_unlock(&ctlr->bus_lock_mutex);

Is not this a bit dangerous? I guess that no one should release the bus
lock without having already released the IO lock but maybe this should
be clearly mentioned in a comment in the original structure definition?

> +
> +		if (ctlr->auto_runtime_pm)
> +			pm_runtime_put(ctlr->dev.parent);
> +
> +		/*
> +		 * Some controllers only optimize specific paths (typically the
> +		 * read path) and expect the core to use the regular SPI
> +		 * interface in these cases.
> +		 */
> +		if (!ret || ret != -ENOTSUPP)
> +			return ret;
> +	}
> +
> +	tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
> +		     op->dummy.nbytes;
> +
> +	/*
> +	 * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
> +	 * we're guaranteed that this buffer is DMA-able, as required by the
> +	 * SPI layer.
> +	 */
> +	tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
> +	if (!tmpbuf)
> +		return -ENOMEM;
> +
> +	spi_message_init(&msg);
> +
> +	tmpbuf[0] = op->cmd.opcode;
> +	xfers[xferpos].tx_buf = tmpbuf;
> +	xfers[xferpos].len = sizeof(op->cmd.opcode);
> +	xfers[xferpos].tx_nbits = op->cmd.buswidth;
> +	spi_message_add_tail(&xfers[xferpos], &msg);
> +	xferpos++;
> +	totalxferlen++;
> +
> +	if (op->addr.nbytes) {
> +		memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);
> +		xfers[xferpos].tx_buf = tmpbuf + 1;
> +		xfers[xferpos].len = op->addr.nbytes;
> +		xfers[xferpos].tx_nbits = op->addr.buswidth;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->addr.nbytes;
> +	}
> +
> +	if (op->dummy.nbytes) {
> +		memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes);
> +		xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
> +		xfers[xferpos].len = op->dummy.nbytes;
> +		xfers[xferpos].tx_nbits = op->dummy.buswidth;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->dummy.nbytes;
> +	}
> +
> +	if (op->data.nbytes) {
> +		if (op->data.dir == SPI_MEM_DATA_IN) {
> +			xfers[xferpos].rx_buf = op->data.buf.in;
> +			xfers[xferpos].rx_nbits = op->data.buswidth;
> +		} else {
> +			xfers[xferpos].tx_buf = op->data.buf.out;
> +			xfers[xferpos].tx_nbits = op->data.buswidth;
> +		}
> +
> +		xfers[xferpos].len = op->data.nbytes;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->data.nbytes;
> +	}
> +
> +	ret = spi_sync(mem->spi, &msg);
> +
> +	kfree(tmpbuf);
> +
> +	if (ret)
> +		return ret;
> +
> +	if (msg.actual_length != totalxferlen)
> +		return -EIO;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_exec_op);
> +
> +/**
> + * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
> + *			      match controller limitations
> + * @mem: the SPI memory
> + * @op: the operation to adjust
> + *
> + * Some controllers have FIFO limitations and must split a data transfer
> + * operation into multiple ones, others require a specific alignment for
> + * optimized accesses. This function allows SPI mem drivers to split a single
> + * operation into multiple sub-operations when required.
> + *
> + * Return: a negative error code if the controller can't properly adjust @op,
> + *	   0 otherwise. Note that @op->data.nbytes will be updated if @op
> + *	   can't be handled in a single step.
> + */
> +int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
> +{
> +	struct spi_controller *ctlr = mem->spi->controller;
> +
> +	if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
> +		return ctlr->mem_ops->adjust_op_size(mem, op);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
> +
> +static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
> +{
> +	return container_of(drv, struct spi_mem_driver, spidrv.driver);
> +}
> +
> +static int spi_mem_probe(struct spi_device *spi)
> +{
> +	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +	struct spi_mem *mem;
> +
> +	mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL);
> +	if (!mem)
> +		return -ENOMEM;
> +
> +	mem->spi = spi;
> +	spi_set_drvdata(spi, mem);
> +
> +	return memdrv->probe(mem);
> +}
> +
> +static int spi_mem_remove(struct spi_device *spi)
> +{
> +	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +	struct spi_mem *mem = spi_get_drvdata(spi);
> +
> +	if (memdrv->remove)
> +		return memdrv->remove(mem);
> +
> +	return 0;
> +}
> +
> +static void spi_mem_shutdown(struct spi_device *spi)
> +{
> +	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +	struct spi_mem *mem = spi_get_drvdata(spi);
> +
> +	if (memdrv->shutdown)
> +		memdrv->shutdown(mem);
> +}
> +
> +/**
> + * spi_mem_driver_register_with_owner() - Register a SPI memory driver
> + * @memdrv: the SPI memory driver to register
> + * @owner: the owner of this driver
> + *
> + * Registers a SPI memory driver.
> + *
> + * Return: 0 in case of success, a negative error core otherwise.
> + */
> +
> +int spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv,
> +				       struct module *owner)
> +{
> +	memdrv->spidrv.probe = spi_mem_probe;
> +	memdrv->spidrv.remove = spi_mem_remove;
> +	memdrv->spidrv.shutdown = spi_mem_shutdown;
> +
> +	return __spi_register_driver(owner, &memdrv->spidrv);
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner);
> +
> +/**
> + * spi_mem_driver_unregister_with_owner() - Unregister a SPI memory driver
> + * @memdrv: the SPI memory driver to unregister
> + *
> + * Unregisters a SPI memory driver.
> + */
> +void spi_mem_driver_unregister(struct spi_mem_driver *memdrv)
> +{
> +	spi_unregister_driver(&memdrv->spidrv);
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_driver_unregister);
> +
>  /*-------------------------------------------------------------------------*/
>  
>  #if IS_ENABLED(CONFIG_OF_DYNAMIC)
> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> index 7b2170bfd6e7..af3c4ac62b55 100644
> --- a/include/linux/spi/spi.h
> +++ b/include/linux/spi/spi.h
> @@ -27,6 +27,7 @@ struct property_entry;
>  struct spi_controller;
>  struct spi_transfer;
>  struct spi_flash_read_message;
> +struct spi_controller_mem_ops;
>  
>  /*
>   * INTERFACES between SPI master-side drivers and SPI slave protocol handlers,
> @@ -376,6 +377,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
>   *                    transfer_one callback.
>   * @handle_err: the subsystem calls the driver to handle an error that occurs
>   *		in the generic implementation of transfer_one_message().
> + * @mem_ops: optimized/dedicated operations for interactions with SPI memory.
> + *	     This field is optional and should only be implemented if the
> + *	     controller has native support for memory like operations.
>   * @unprepare_message: undo any work done by prepare_message().
>   * @slave_abort: abort the ongoing transfer request on an SPI slave controller
>   * @spi_flash_read: to support spi-controller hardwares that provide
> @@ -564,6 +568,9 @@ struct spi_controller {
>  	void (*handle_err)(struct spi_controller *ctlr,
>  			   struct spi_message *message);
>  
> +	/* Optimized handlers for SPI memory-like operations. */
> +	const struct spi_controller_mem_ops *mem_ops;
> +
>  	/* gpio chip select */
>  	int			*cs_gpios;
>  
> @@ -1227,6 +1234,225 @@ int spi_flash_read(struct spi_device *spi,
>  
>  /*---------------------------------------------------------------------------*/
>  
> +/* SPI memory related definitions. */
> +
> +#define SPI_MEM_OP_CMD(__opcode, __buswidth)			\
> +	{							\
> +		.buswidth = __buswidth,				\
> +		.opcode = __opcode,				\
> +	}
> +
> +#define SPI_MEM_OP_ADDRS(__nbytes, __buf, __buswidth)		\
> +	{							\
> +		.nbytes = __nbytes,				\
> +		.buf = __buf,					\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_NO_ADDRS	{ }
> +
> +#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth)			\
> +	{							\
> +		.nbytes = __nbytes,				\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_NO_DUMMY	{ }
> +
> +#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth)		\
> +	{							\
> +		.dir = SPI_MEM_DATA_IN,				\
> +		.nbytes = __nbytes,				\
> +		.buf.in = __buf,				\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth)	\
> +	{							\
> +		.dir = SPI_MEM_DATA_OUT,			\
> +		.nbytes = __nbytes,				\
> +		.buf.out = __buf,				\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_NO_DATA	{ }
> +
> +/**
> + * enum spi_mem_data_dir - describes the direction of a SPI memory data
> + *			   transfer from the controller perspective
> + * @SPI_MEM_DATA_IN: data coming from the SPI memory
> + * @SPI_MEM_DATA_OUT: data sent the SPI memory
> + */
> +enum spi_mem_data_dir {
> +	SPI_MEM_DATA_IN,
> +	SPI_MEM_DATA_OUT,
> +};
> +
> +/**
> + * struct spi_mem_op - describes a SPI memory operation
> + * @cmd.buswidth: number of IO lines used to transmit the command
> + * @cmd.opcode: operation opcode
> + * @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.buf: buffer storing the address bytes
> + * @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
> + * @data.buswidth: number of IO lanes used to send/receive the data
> + * @data.dir: direction of the transfer
> + * @data.buf.in: input buffer
> + * @data.buf.out: output buffer
> + */
> +struct spi_mem_op {
> +	struct {
> +		u8 buswidth;
> +		u8 opcode;
> +	} cmd;
> +
> +	struct {
> +		u8 nbytes;
> +		u8 buswidth;
> +		const u8 *buf;
> +	} addr;
> +
> +	struct {
> +		u8 nbytes;
> +		u8 buswidth;
> +	} dummy;
> +
> +	struct {
> +		u8 buswidth;
> +		enum spi_mem_data_dir dir;
> +		unsigned int nbytes;
> +		/* buf.{in,out} must be DMA-able. */
> +		union {
> +			void *in;
> +			const void *out;
> +		} buf;
> +	} data;
> +};
> +
> +#define SPI_MEM_OP(__cmd, __addr, __dummy, __data)		\
> +	{							\
> +		.cmd = __cmd,					\
> +		.addr = __addr,					\
> +		.dummy = __dummy,				\
> +		.data = __data,					\
> +	}
> +
> +/**
> + * struct spi_mem - describes a SPI memory device
> + * @spi: the underlying SPI device
> + * @drvpriv: spi_mem_drviver private data
> + *
> + * Extra information that describe the SPI memory device and may be needed by
> + * the controller to properly handle this device should be placed here.
> + *
> + * One example would be the device size since some controller expose their SPI
> + * mem devices through a io-mapped region.
> + */
> +struct spi_mem {
> +	struct spi_device *spi;
> +	void *drvpriv;
> +};
> +
> +/**
> + * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
> + *				  device
> + * @mem: memory device
> + * @data: data to attach to the memory device
> + */
> +static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
> +{
> +	mem->drvpriv = data;
> +}
> +
> +/**
> + * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
> + *				  device
> + * @mem: memory device
> + *
> + * Return: the data attached to the mem device.
> + */
> +static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
> +{
> +	return mem->drvpriv;
> +}
> +
> +/**
> + * 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)
> + * @supports_op: check if an operation is supported by the controller
> + * @exec_op: execute a SPI memory operation
> + *
> + * 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.
> + */
> +struct spi_controller_mem_ops {
> +	int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
> +	bool (*supports_op)(struct spi_mem *mem,
> +			    const struct spi_mem_op *op);
> +	int (*exec_op)(struct spi_mem *mem,
> +		       const struct spi_mem_op *op);

:)

> +};
> +
> +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
> +				       const struct spi_mem_op *op,
> +				       struct sg_table *sg);
> +
> +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> +					  const struct spi_mem_op *op,
> +					  struct sg_table *sg);
> +
> +int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
> +
> +bool spi_mem_supports_op(struct spi_mem *mem,
> +			 const struct spi_mem_op *op);
> +
> +int spi_mem_exec_op(struct spi_mem *mem,
> +		    const struct spi_mem_op *op);
> +
> +/**
> + * struct spi_mem_driver - SPI memory driver
> + * @spidrv: inherit from a SPI driver
> + * @probe: probe a SPI memory. Usually where detection/initialization takes
> + *	   place
> + * @remove: remove a SPI memory
> + * @shutdown: take appropriate action when the system is shutdown
> + *
> + * This is just a thin wrapper around a spi_driver. The core takes care of
> + * allocating the spi_mem object and forwarding the probe/remove/shutdown
> + * request to the spi_mem_driver. The reason we use this wrapper is because
> + * we might have to stuff more information into the spi_mem struct to let
> + * SPI controllers know more about the SPI memory they interact with, and
> + * having this intermediate layer allows us to do that without adding more
> + * useless fields to the spi_device object.
> + */
> +struct spi_mem_driver {
> +	struct spi_driver spidrv;
> +	int (*probe)(struct spi_mem *mem);
> +	int (*remove)(struct spi_mem *mem);
> +	void (*shutdown)(struct spi_mem *mem);
> +};
> +
> +int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
> +				       struct module *owner);
> +
> +#define spi_mem_driver_register(__drv)                                  \
> +	spi_mem_driver_register_with_owner(__drv, THIS_MODULE)
> +
> +void spi_mem_driver_unregister(struct spi_mem_driver *drv);
> +
> +#define module_spi_mem_driver(__drv)                                    \
> +	module_driver(__drv, spi_mem_driver_register,                   \
> +		      spi_mem_driver_unregister)
> +
> +/*---------------------------------------------------------------------------*/
> +
>  /*
>   * INTERFACE between board init code and SPI infrastructure.
>   *


Best regards,
Miquèl

-- 
Miquel Raynal, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-05 23:21     ` Boris Brezillon
@ 2018-02-11 15:17         ` Miquel Raynal
  -1 siblings, 0 replies; 92+ messages in thread
From: Miquel Raynal @ 2018-02-11 15:17 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Yogesh Gaur, Vignesh R,
	Kamal Dasu, Peter Pan, Frieder Schrempf, Rafał Miłecki,
	Sourav Poddar

Hi Boris,

On Tue,  6 Feb 2018 00:21:18 +0100, Boris Brezillon
<boris.brezillon-LDxbnhwyfcJBDgjK7y7TUQ@public.gmane.org> wrote:

> From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> 
> The spi_mem interface is meant to replace the spi_flash_read() one.
> Implement the ->exec_op() method so that we can smoothly get rid of the
> old interface.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> ---
>  drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 72 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> index c24d9b45a27c..40cac3ef6cc9 100644
> --- a/drivers/spi/spi-ti-qspi.c
> +++ b/drivers/spi/spi-ti-qspi.c
> @@ -434,12 +434,10 @@ static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst,
>  	return 0;
>  }
>  
> -static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi,
> -				     struct spi_flash_read_message *msg)
> +static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi, loff_t offs,
> +				     void *to, size_t readsize)
>  {
> -	size_t readsize = msg->len;
> -	void *to = msg->buf;
> -	dma_addr_t dma_src = qspi->mmap_phys_base + msg->from;
> +	dma_addr_t dma_src = qspi->mmap_phys_base + offs;
>  	int ret = 0;
>  
>  	/*
> @@ -507,13 +505,14 @@ static void ti_qspi_disable_memory_map(struct spi_device *spi)
>  	qspi->mmap_enabled = false;
>  }
>  
> -static void ti_qspi_setup_mmap_read(struct spi_device *spi,
> -				    struct spi_flash_read_message *msg)
> +static void ti_qspi_setup_mmap_read(struct spi_device *spi, u8 opcode,
> +				    u8 data_nbits, u8 addr_width,
> +				    u8 dummy_bytes)
>  {
>  	struct ti_qspi  *qspi = spi_master_get_devdata(spi->master);
> -	u32 memval = msg->read_opcode;
> +	u32 memval = opcode;
>  
> -	switch (msg->data_nbits) {
> +	switch (data_nbits) {
>  	case SPI_NBITS_QUAD:
>  		memval |= QSPI_SETUP_RD_QUAD;
>  		break;
> @@ -524,8 +523,8 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi,
>  		memval |= QSPI_SETUP_RD_NORMAL;
>  		break;
>  	}
> -	memval |= ((msg->addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
> -		   msg->dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
> +	memval |= ((addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
> +		   dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
>  	ti_qspi_write(qspi, memval,
>  		      QSPI_SPI_SETUP_REG(spi->chip_select));
>  }
> @@ -546,13 +545,15 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
>  
>  	if (!qspi->mmap_enabled)
>  		ti_qspi_enable_memory_map(spi);
> -	ti_qspi_setup_mmap_read(spi, msg);
> +	ti_qspi_setup_mmap_read(spi, msg->read_opcode, msg->data_nbits,
> +				msg->addr_width, msg->dummy_bytes);
>  
>  	if (qspi->rx_chan) {
>  		if (msg->cur_msg_mapped)
>  			ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from);
>  		else
> -			ret = ti_qspi_dma_bounce_buffer(qspi, msg);
> +			ret = ti_qspi_dma_bounce_buffer(qspi, msg->from,
> +							msg->buf, msg->len);
>  		if (ret)
>  			goto err_unlock;
>  	} else {
> @@ -566,6 +567,62 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
>  	return ret;
>  }
>  
> +static int ti_qspi_exec_mem_op(struct spi_mem *mem,
> +			       const struct spi_mem_op *op)
> +{
> +	struct ti_qspi *qspi = spi_master_get_devdata(mem->spi->master);
> +	int i, ret = 0;
> +	u32 from = 0;
> +
> +	/* Only optimize read path. */
> +	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
> +	    !op->addr.nbytes || op->addr.nbytes > 4)
> +		return -ENOTSUPP;
> +
> +	for (i = 0; i < op->addr.nbytes; i++) {
> +		from <<= 8;
> +		from |= op->addr.buf[i];
> +	}
> +
> +	/* Address exceeds MMIO window size, fall back to regular mode. */

I don't understand how do you fall back to regular mode? Moreover if the
purpose of adding this function is to remove spi_flash_read().

> +	if (from > 0x4000000)
> +		return -ENOTSUPP;
> +
> +	mutex_lock(&qspi->list_lock);
> +
> +	if (!qspi->mmap_enabled)
> +		ti_qspi_enable_memory_map(mem->spi);
> +	ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
> +				op->addr.nbytes, op->dummy.nbytes);
> +
> +	if (qspi->rx_chan) {
> +		struct sg_table sgt;
> +
> +		if (!virt_addr_valid(op->data.buf.in) &&
> +		    !spi_controller_dma_map_mem_op_data(mem->spi->master, op,
> +							&sgt)) {
> +			ret = ti_qspi_dma_xfer_sg(qspi, sgt, from);
> +			spi_controller_dma_unmap_mem_op_data(mem->spi->master,
> +							     op, &sgt);
> +		} else {
> +			ret = ti_qspi_dma_bounce_buffer(qspi, from,
> +							op->data.buf.in,
> +							op->data.nbytes);
> +		}
> +	} else {
> +		memcpy_fromio(op->data.buf.in, qspi->mmap_base + from,
> +			      op->data.nbytes);
> +	}
> +
> +	mutex_unlock(&qspi->list_lock);
> +
> +	return ret;
> +}
> +
> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> +	.exec_op = ti_qspi_exec_mem_op,
> +};
> +
>  static int ti_qspi_start_transfer_one(struct spi_master *master,
>  		struct spi_message *m)
>  {
> @@ -673,6 +730,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
>  	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
>  				     SPI_BPW_MASK(8);
>  	master->spi_flash_read = ti_qspi_spi_flash_read;
> +	master->mem_ops = &ti_qspi_mem_ops;
>  
>  	if (!of_property_read_u32(np, "num-cs", &num_cs))
>  		master->num_chipselect = num_cs;
> @@ -785,6 +843,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
>  				 PTR_ERR(qspi->mmap_base));
>  			qspi->mmap_base = NULL;
>  			master->spi_flash_read = NULL;
> +			master->mem_ops = NULL;
>  		}
>  	}
>  	qspi->mmap_enabled = false;



-- 
Miquel Raynal, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-11 15:17         ` Miquel Raynal
  0 siblings, 0 replies; 92+ messages in thread
From: Miquel Raynal @ 2018-02-11 15:17 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Yogesh Gaur, Vignesh R, Kamal Dasu, Peter Pan,
	Frieder Schrempf, Rafał Miłecki, Sourav Poddar

Hi Boris,

On Tue,  6 Feb 2018 00:21:18 +0100, Boris Brezillon
<boris.brezillon@bootlin.com> wrote:

> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> 
> The spi_mem interface is meant to replace the spi_flash_read() one.
> Implement the ->exec_op() method so that we can smoothly get rid of the
> old interface.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> ---
>  drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 72 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> index c24d9b45a27c..40cac3ef6cc9 100644
> --- a/drivers/spi/spi-ti-qspi.c
> +++ b/drivers/spi/spi-ti-qspi.c
> @@ -434,12 +434,10 @@ static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst,
>  	return 0;
>  }
>  
> -static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi,
> -				     struct spi_flash_read_message *msg)
> +static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi, loff_t offs,
> +				     void *to, size_t readsize)
>  {
> -	size_t readsize = msg->len;
> -	void *to = msg->buf;
> -	dma_addr_t dma_src = qspi->mmap_phys_base + msg->from;
> +	dma_addr_t dma_src = qspi->mmap_phys_base + offs;
>  	int ret = 0;
>  
>  	/*
> @@ -507,13 +505,14 @@ static void ti_qspi_disable_memory_map(struct spi_device *spi)
>  	qspi->mmap_enabled = false;
>  }
>  
> -static void ti_qspi_setup_mmap_read(struct spi_device *spi,
> -				    struct spi_flash_read_message *msg)
> +static void ti_qspi_setup_mmap_read(struct spi_device *spi, u8 opcode,
> +				    u8 data_nbits, u8 addr_width,
> +				    u8 dummy_bytes)
>  {
>  	struct ti_qspi  *qspi = spi_master_get_devdata(spi->master);
> -	u32 memval = msg->read_opcode;
> +	u32 memval = opcode;
>  
> -	switch (msg->data_nbits) {
> +	switch (data_nbits) {
>  	case SPI_NBITS_QUAD:
>  		memval |= QSPI_SETUP_RD_QUAD;
>  		break;
> @@ -524,8 +523,8 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi,
>  		memval |= QSPI_SETUP_RD_NORMAL;
>  		break;
>  	}
> -	memval |= ((msg->addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
> -		   msg->dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
> +	memval |= ((addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
> +		   dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
>  	ti_qspi_write(qspi, memval,
>  		      QSPI_SPI_SETUP_REG(spi->chip_select));
>  }
> @@ -546,13 +545,15 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
>  
>  	if (!qspi->mmap_enabled)
>  		ti_qspi_enable_memory_map(spi);
> -	ti_qspi_setup_mmap_read(spi, msg);
> +	ti_qspi_setup_mmap_read(spi, msg->read_opcode, msg->data_nbits,
> +				msg->addr_width, msg->dummy_bytes);
>  
>  	if (qspi->rx_chan) {
>  		if (msg->cur_msg_mapped)
>  			ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from);
>  		else
> -			ret = ti_qspi_dma_bounce_buffer(qspi, msg);
> +			ret = ti_qspi_dma_bounce_buffer(qspi, msg->from,
> +							msg->buf, msg->len);
>  		if (ret)
>  			goto err_unlock;
>  	} else {
> @@ -566,6 +567,62 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
>  	return ret;
>  }
>  
> +static int ti_qspi_exec_mem_op(struct spi_mem *mem,
> +			       const struct spi_mem_op *op)
> +{
> +	struct ti_qspi *qspi = spi_master_get_devdata(mem->spi->master);
> +	int i, ret = 0;
> +	u32 from = 0;
> +
> +	/* Only optimize read path. */
> +	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
> +	    !op->addr.nbytes || op->addr.nbytes > 4)
> +		return -ENOTSUPP;
> +
> +	for (i = 0; i < op->addr.nbytes; i++) {
> +		from <<= 8;
> +		from |= op->addr.buf[i];
> +	}
> +
> +	/* Address exceeds MMIO window size, fall back to regular mode. */

I don't understand how do you fall back to regular mode? Moreover if the
purpose of adding this function is to remove spi_flash_read().

> +	if (from > 0x4000000)
> +		return -ENOTSUPP;
> +
> +	mutex_lock(&qspi->list_lock);
> +
> +	if (!qspi->mmap_enabled)
> +		ti_qspi_enable_memory_map(mem->spi);
> +	ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
> +				op->addr.nbytes, op->dummy.nbytes);
> +
> +	if (qspi->rx_chan) {
> +		struct sg_table sgt;
> +
> +		if (!virt_addr_valid(op->data.buf.in) &&
> +		    !spi_controller_dma_map_mem_op_data(mem->spi->master, op,
> +							&sgt)) {
> +			ret = ti_qspi_dma_xfer_sg(qspi, sgt, from);
> +			spi_controller_dma_unmap_mem_op_data(mem->spi->master,
> +							     op, &sgt);
> +		} else {
> +			ret = ti_qspi_dma_bounce_buffer(qspi, from,
> +							op->data.buf.in,
> +							op->data.nbytes);
> +		}
> +	} else {
> +		memcpy_fromio(op->data.buf.in, qspi->mmap_base + from,
> +			      op->data.nbytes);
> +	}
> +
> +	mutex_unlock(&qspi->list_lock);
> +
> +	return ret;
> +}
> +
> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> +	.exec_op = ti_qspi_exec_mem_op,
> +};
> +
>  static int ti_qspi_start_transfer_one(struct spi_master *master,
>  		struct spi_message *m)
>  {
> @@ -673,6 +730,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
>  	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
>  				     SPI_BPW_MASK(8);
>  	master->spi_flash_read = ti_qspi_spi_flash_read;
> +	master->mem_ops = &ti_qspi_mem_ops;
>  
>  	if (!of_property_read_u32(np, "num-cs", &num_cs))
>  		master->num_chipselect = num_cs;
> @@ -785,6 +843,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
>  				 PTR_ERR(qspi->mmap_base));
>  			qspi->mmap_base = NULL;
>  			master->spi_flash_read = NULL;
> +			master->mem_ops = NULL;
>  		}
>  	}
>  	qspi->mmap_enabled = false;



-- 
Miquel Raynal, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-09 12:52         ` Miquel Raynal
@ 2018-02-11 16:00           ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-11 16:00 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Yogesh Gaur, Vignesh R,
	Kamal Dasu, Peter Pan, Frieder Schrempf, Rafał Miłecki,
	Sourav Poddar

Hi Miquel,

On Fri, 9 Feb 2018 13:52:05 +0100
Miquel Raynal <miquel.raynal-LDxbnhwyfcJBDgjK7y7TUQ@public.gmane.org> wrote:

> > +/**
> > + * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a
> > + *					    memory operation
> > + * @ctlr: the SPI controller requesting this dma_unmap()
> > + * @op: the memory operation containing the buffer to unmap
> > + * @sgt: a pointer to an sg_table previously initialized by
> > + *	 spi_controller_dma_map_mem_op_data()
> > + *
> > + * Some controllers might want to do DMA on the data buffer embedded in @op.
> > + * This helper prepares things so that the CPU can access the
> > + * op->data.buf.{in,out} buffer again.
> > + *
> > + * This function is not intended to be called from spi drivers. Only SPI  
> 
> s/spi/SPI/
> 
> > + * controller drivers should use it.
> > + *
> > + * This function should be called after the DMA operation has finished an is  
> 
> s/an/and/

Will fix both.

> 
> > + * only valid if the previous spi_controller_dma_map_mem_op_data() has returned
> > + * 0.
> > + *
> > + * Return: 0 in case of success, a negative error code otherwise.
> > + */
> > +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> > +					  const struct spi_mem_op *op,
> > +					  struct sg_table *sgt)
> > +{
> > +	struct device *dmadev;
> > +
> > +	if (!op->data.nbytes)
> > +		return;
> > +
> > +	if (op->data.dir == SPI_MEM_DATA_OUT)
> > +		dmadev = ctlr->dma_tx ?
> > +			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
> > +	else
> > +		dmadev = ctlr->dma_rx ?
> > +			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
> > +
> > +	spi_unmap_buf(ctlr, dmadev, sgt,
> > +		      op->data.dir == SPI_MEM_DATA_IN ?
> > +		      DMA_FROM_DEVICE : DMA_TO_DEVICE);
> > +}
> > +EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data);
> > +
> > +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
> > +{
> > +	u32 mode = mem->spi->mode;
> > +
> > +	switch (buswidth) {
> > +	case 1:
> > +		return 0;
> > +
> > +	case 2:
> > +		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> > +		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
> > +			return 0;
> > +
> > +		break;
> > +
> > +	case 4:
> > +		if ((tx && (mode & SPI_TX_QUAD)) ||
> > +		    (!tx && (mode & SPI_RX_QUAD)))
> > +			return 0;
> > +
> > +		break;
> > +
> > +	default:
> > +		break;
> > +	}
> > +
> > +	return -ENOTSUPP;
> > +}
> > +
> > +/**
> > + * spi_mem_supports_op() - Check if a memory device and the controller it is
> > + *			   connected to support a specific memory operation
> > + * @mem: the SPI memory
> > + * @op: the memory operation to check
> > + *
> > + * Some controllers are only supporting Single or Dual IOs, others might only
> > + * support specific opcodes, or it can even be that the controller and device
> > + * both support Quad IOs but the hardware prevents you from using it because
> > + * only 2 IO lines are connected.
> > + *
> > + * This function checks whether a specific operation is supported.
> > + *
> > + * Return: true if @op is supported, false otherwise.
> > + */
> > +bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
> > +{
> > +	struct spi_controller *ctlr = mem->spi->controller;
> > +
> > +	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
> > +		return false;
> > +
> > +	if (op->addr.nbytes &&
> > +	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
> > +		return false;
> > +
> > +	if (op->dummy.nbytes &&
> > +	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
> > +		return false;
> > +
> > +	if (op->data.nbytes &&
> > +	    spi_check_buswidth_req(mem, op->data.buswidth,
> > +				   op->data.dir == SPI_MEM_DATA_IN ?
> > +				   false : true))  
> 
> Why not just op->data.dir != SPI_MEM_DATA_IN or even better ==
> SPI_MEM_DATA_OUT if it exists?

Indeed, I'll use op->data.dir == SPI_MEM_DATA_OUT.

> 
> > +		return false;
> > +
> > +	if (ctlr->mem_ops)
> > +		return ctlr->mem_ops->supports_op(mem, op);
> > +
> > +	return true;
> > +}
> > +EXPORT_SYMBOL_GPL(spi_mem_supports_op);
> > +
> > +/**
> > + * spi_mem_exec_op() - Execute a memory operation
> > + * @mem: the SPI memory
> > + * @op: the memory operation to execute
> > + *
> > + * Executes a memory operation.
> > + *
> > + * This function first checks that @op is supported and then tries to execute
> > + * it.
> > + *
> > + * Return: 0 in case of success, a negative error code otherwise.
> > + */
> > +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> > +{
> > +	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> > +	struct spi_controller *ctlr = mem->spi->controller;
> > +	struct spi_transfer xfers[4] = { };
> > +	struct spi_message msg;
> > +	u8 *tmpbuf;
> > +	int ret;
> > +
> > +	if (!spi_mem_supports_op(mem, op))
> > +		return -ENOTSUPP;
> > +
> > +	if (ctlr->mem_ops) {
> > +		if (ctlr->auto_runtime_pm) {
> > +			ret = pm_runtime_get_sync(ctlr->dev.parent);
> > +			if (ret < 0) {
> > +				dev_err(&ctlr->dev,
> > +					"Failed to power device: %d\n",
> > +					ret);
> > +				return ret;
> > +			}
> > +		}
> > +
> > +		mutex_lock(&ctlr->bus_lock_mutex);
> > +		mutex_lock(&ctlr->io_mutex);
> > +		ret = ctlr->mem_ops->exec_op(mem, op);
> > +		mutex_unlock(&ctlr->io_mutex);
> > +		mutex_unlock(&ctlr->bus_lock_mutex);  
> 
> Is not this a bit dangerous? I guess that no one should release the bus
> lock without having already released the IO lock but maybe this should
> be clearly mentioned in a comment in the original structure definition?

It's not something new, spi_flash_read() was doing the same [1]. This
being said, if Mark agrees, I can add a comment in the spi_controller
struct def stating that ->bus_lock_mutex should always be acquired
before ->io_mutex.

Thanks for your review.

Boris

[1]http://elixir.free-electrons.com/linux/v4.15.2/source/drivers/spi/spi.c#L3045
-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-02-11 16:00           ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-11 16:00 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Yogesh Gaur, Vignesh R, Kamal Dasu, Peter Pan,
	Frieder Schrempf, Rafał Miłecki, Sourav Poddar

Hi Miquel,

On Fri, 9 Feb 2018 13:52:05 +0100
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> > +/**
> > + * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a
> > + *					    memory operation
> > + * @ctlr: the SPI controller requesting this dma_unmap()
> > + * @op: the memory operation containing the buffer to unmap
> > + * @sgt: a pointer to an sg_table previously initialized by
> > + *	 spi_controller_dma_map_mem_op_data()
> > + *
> > + * Some controllers might want to do DMA on the data buffer embedded in @op.
> > + * This helper prepares things so that the CPU can access the
> > + * op->data.buf.{in,out} buffer again.
> > + *
> > + * This function is not intended to be called from spi drivers. Only SPI  
> 
> s/spi/SPI/
> 
> > + * controller drivers should use it.
> > + *
> > + * This function should be called after the DMA operation has finished an is  
> 
> s/an/and/

Will fix both.

> 
> > + * only valid if the previous spi_controller_dma_map_mem_op_data() has returned
> > + * 0.
> > + *
> > + * Return: 0 in case of success, a negative error code otherwise.
> > + */
> > +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> > +					  const struct spi_mem_op *op,
> > +					  struct sg_table *sgt)
> > +{
> > +	struct device *dmadev;
> > +
> > +	if (!op->data.nbytes)
> > +		return;
> > +
> > +	if (op->data.dir == SPI_MEM_DATA_OUT)
> > +		dmadev = ctlr->dma_tx ?
> > +			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
> > +	else
> > +		dmadev = ctlr->dma_rx ?
> > +			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
> > +
> > +	spi_unmap_buf(ctlr, dmadev, sgt,
> > +		      op->data.dir == SPI_MEM_DATA_IN ?
> > +		      DMA_FROM_DEVICE : DMA_TO_DEVICE);
> > +}
> > +EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data);
> > +
> > +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
> > +{
> > +	u32 mode = mem->spi->mode;
> > +
> > +	switch (buswidth) {
> > +	case 1:
> > +		return 0;
> > +
> > +	case 2:
> > +		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> > +		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
> > +			return 0;
> > +
> > +		break;
> > +
> > +	case 4:
> > +		if ((tx && (mode & SPI_TX_QUAD)) ||
> > +		    (!tx && (mode & SPI_RX_QUAD)))
> > +			return 0;
> > +
> > +		break;
> > +
> > +	default:
> > +		break;
> > +	}
> > +
> > +	return -ENOTSUPP;
> > +}
> > +
> > +/**
> > + * spi_mem_supports_op() - Check if a memory device and the controller it is
> > + *			   connected to support a specific memory operation
> > + * @mem: the SPI memory
> > + * @op: the memory operation to check
> > + *
> > + * Some controllers are only supporting Single or Dual IOs, others might only
> > + * support specific opcodes, or it can even be that the controller and device
> > + * both support Quad IOs but the hardware prevents you from using it because
> > + * only 2 IO lines are connected.
> > + *
> > + * This function checks whether a specific operation is supported.
> > + *
> > + * Return: true if @op is supported, false otherwise.
> > + */
> > +bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
> > +{
> > +	struct spi_controller *ctlr = mem->spi->controller;
> > +
> > +	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
> > +		return false;
> > +
> > +	if (op->addr.nbytes &&
> > +	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
> > +		return false;
> > +
> > +	if (op->dummy.nbytes &&
> > +	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
> > +		return false;
> > +
> > +	if (op->data.nbytes &&
> > +	    spi_check_buswidth_req(mem, op->data.buswidth,
> > +				   op->data.dir == SPI_MEM_DATA_IN ?
> > +				   false : true))  
> 
> Why not just op->data.dir != SPI_MEM_DATA_IN or even better ==
> SPI_MEM_DATA_OUT if it exists?

Indeed, I'll use op->data.dir == SPI_MEM_DATA_OUT.

> 
> > +		return false;
> > +
> > +	if (ctlr->mem_ops)
> > +		return ctlr->mem_ops->supports_op(mem, op);
> > +
> > +	return true;
> > +}
> > +EXPORT_SYMBOL_GPL(spi_mem_supports_op);
> > +
> > +/**
> > + * spi_mem_exec_op() - Execute a memory operation
> > + * @mem: the SPI memory
> > + * @op: the memory operation to execute
> > + *
> > + * Executes a memory operation.
> > + *
> > + * This function first checks that @op is supported and then tries to execute
> > + * it.
> > + *
> > + * Return: 0 in case of success, a negative error code otherwise.
> > + */
> > +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> > +{
> > +	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> > +	struct spi_controller *ctlr = mem->spi->controller;
> > +	struct spi_transfer xfers[4] = { };
> > +	struct spi_message msg;
> > +	u8 *tmpbuf;
> > +	int ret;
> > +
> > +	if (!spi_mem_supports_op(mem, op))
> > +		return -ENOTSUPP;
> > +
> > +	if (ctlr->mem_ops) {
> > +		if (ctlr->auto_runtime_pm) {
> > +			ret = pm_runtime_get_sync(ctlr->dev.parent);
> > +			if (ret < 0) {
> > +				dev_err(&ctlr->dev,
> > +					"Failed to power device: %d\n",
> > +					ret);
> > +				return ret;
> > +			}
> > +		}
> > +
> > +		mutex_lock(&ctlr->bus_lock_mutex);
> > +		mutex_lock(&ctlr->io_mutex);
> > +		ret = ctlr->mem_ops->exec_op(mem, op);
> > +		mutex_unlock(&ctlr->io_mutex);
> > +		mutex_unlock(&ctlr->bus_lock_mutex);  
> 
> Is not this a bit dangerous? I guess that no one should release the bus
> lock without having already released the IO lock but maybe this should
> be clearly mentioned in a comment in the original structure definition?

It's not something new, spi_flash_read() was doing the same [1]. This
being said, if Mark agrees, I can add a comment in the spi_controller
struct def stating that ->bus_lock_mutex should always be acquired
before ->io_mutex.

Thanks for your review.

Boris

[1]http://elixir.free-electrons.com/linux/v4.15.2/source/drivers/spi/spi.c#L3045
-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-11 15:17         ` Miquel Raynal
@ 2018-02-11 17:18           ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-11 17:18 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Yogesh Gaur, Vignesh R,
	Kamal Dasu, Peter Pan, Frieder Schrempf, Rafał Miłecki,
	Sourav Poddar

On Sun, 11 Feb 2018 16:17:06 +0100
Miquel Raynal <miquel.raynal-LDxbnhwyfcJBDgjK7y7TUQ@public.gmane.org> wrote:

> Hi Boris,
> 
> On Tue,  6 Feb 2018 00:21:18 +0100, Boris Brezillon
> <boris.brezillon-LDxbnhwyfcJBDgjK7y7TUQ@public.gmane.org> wrote:
> 
> > From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > 
> > The spi_mem interface is meant to replace the spi_flash_read() one.
> > Implement the ->exec_op() method so that we can smoothly get rid of the
> > old interface.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > ---
> >  drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
> >  1 file changed, 72 insertions(+), 13 deletions(-)
> > 
> > diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> > index c24d9b45a27c..40cac3ef6cc9 100644
> > --- a/drivers/spi/spi-ti-qspi.c
> > +++ b/drivers/spi/spi-ti-qspi.c
> > @@ -434,12 +434,10 @@ static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst,
> >  	return 0;
> >  }
> >  
> > -static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi,
> > -				     struct spi_flash_read_message *msg)
> > +static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi, loff_t offs,
> > +				     void *to, size_t readsize)
> >  {
> > -	size_t readsize = msg->len;
> > -	void *to = msg->buf;
> > -	dma_addr_t dma_src = qspi->mmap_phys_base + msg->from;
> > +	dma_addr_t dma_src = qspi->mmap_phys_base + offs;
> >  	int ret = 0;
> >  
> >  	/*
> > @@ -507,13 +505,14 @@ static void ti_qspi_disable_memory_map(struct spi_device *spi)
> >  	qspi->mmap_enabled = false;
> >  }
> >  
> > -static void ti_qspi_setup_mmap_read(struct spi_device *spi,
> > -				    struct spi_flash_read_message *msg)
> > +static void ti_qspi_setup_mmap_read(struct spi_device *spi, u8 opcode,
> > +				    u8 data_nbits, u8 addr_width,
> > +				    u8 dummy_bytes)
> >  {
> >  	struct ti_qspi  *qspi = spi_master_get_devdata(spi->master);
> > -	u32 memval = msg->read_opcode;
> > +	u32 memval = opcode;
> >  
> > -	switch (msg->data_nbits) {
> > +	switch (data_nbits) {
> >  	case SPI_NBITS_QUAD:
> >  		memval |= QSPI_SETUP_RD_QUAD;
> >  		break;
> > @@ -524,8 +523,8 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi,
> >  		memval |= QSPI_SETUP_RD_NORMAL;
> >  		break;
> >  	}
> > -	memval |= ((msg->addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
> > -		   msg->dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
> > +	memval |= ((addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
> > +		   dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
> >  	ti_qspi_write(qspi, memval,
> >  		      QSPI_SPI_SETUP_REG(spi->chip_select));
> >  }
> > @@ -546,13 +545,15 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
> >  
> >  	if (!qspi->mmap_enabled)
> >  		ti_qspi_enable_memory_map(spi);
> > -	ti_qspi_setup_mmap_read(spi, msg);
> > +	ti_qspi_setup_mmap_read(spi, msg->read_opcode, msg->data_nbits,
> > +				msg->addr_width, msg->dummy_bytes);
> >  
> >  	if (qspi->rx_chan) {
> >  		if (msg->cur_msg_mapped)
> >  			ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from);
> >  		else
> > -			ret = ti_qspi_dma_bounce_buffer(qspi, msg);
> > +			ret = ti_qspi_dma_bounce_buffer(qspi, msg->from,
> > +							msg->buf, msg->len);
> >  		if (ret)
> >  			goto err_unlock;
> >  	} else {
> > @@ -566,6 +567,62 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
> >  	return ret;
> >  }
> >  
> > +static int ti_qspi_exec_mem_op(struct spi_mem *mem,
> > +			       const struct spi_mem_op *op)
> > +{
> > +	struct ti_qspi *qspi = spi_master_get_devdata(mem->spi->master);
> > +	int i, ret = 0;
> > +	u32 from = 0;
> > +
> > +	/* Only optimize read path. */
> > +	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
> > +	    !op->addr.nbytes || op->addr.nbytes > 4)
> > +		return -ENOTSUPP;
> > +
> > +	for (i = 0; i < op->addr.nbytes; i++) {
> > +		from <<= 8;
> > +		from |= op->addr.buf[i];
> > +	}
> > +
> > +	/* Address exceeds MMIO window size, fall back to regular mode. */  
> 
> I don't understand how do you fall back to regular mode?

If you look at spi_mem_exec_op() you'll see that if the controller
->exec_op() returns -ENOTSUPP, the function will try to execute the
operation using the regular spi_sync() API. I'll try to make it clearer
in my next iteration.

> Moreover if the
> purpose of adding this function is to remove spi_flash_read().

Sorry, I don't get that one. Yes, the spi_mem_ops interface is here to
replace the ->spi_flash_xx() one, and that's exactly what I'm doing
here: porting the existing implementation to the new interface, keeping
the exact same limitations (only read path is optimized, and the request
has to fall in the iomem range mapped by the driver).

> 
> > +	if (from > 0x4000000)

Oops! Looks like I forgot to update the code to store the qspi_mmap
resource size in ti_qspi struct and use it here to detect whether
mmap access is allowed or not. Will fix that in v2.

> > +		return -ENOTSUPP;
> > +
> > +	mutex_lock(&qspi->list_lock);
> > +
> > +	if (!qspi->mmap_enabled)
> > +		ti_qspi_enable_memory_map(mem->spi);
> > +	ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
> > +				op->addr.nbytes, op->dummy.nbytes);
> > +
> > +	if (qspi->rx_chan) {
> > +		struct sg_table sgt;
> > +
> > +		if (!virt_addr_valid(op->data.buf.in) &&
> > +		    !spi_controller_dma_map_mem_op_data(mem->spi->master, op,
> > +							&sgt)) {
> > +			ret = ti_qspi_dma_xfer_sg(qspi, sgt, from);
> > +			spi_controller_dma_unmap_mem_op_data(mem->spi->master,
> > +							     op, &sgt);
> > +		} else {
> > +			ret = ti_qspi_dma_bounce_buffer(qspi, from,
> > +							op->data.buf.in,
> > +							op->data.nbytes);
> > +		}
> > +	} else {
> > +		memcpy_fromio(op->data.buf.in, qspi->mmap_base + from,
> > +			      op->data.nbytes);
> > +	}
> > +
> > +	mutex_unlock(&qspi->list_lock);
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> > +	.exec_op = ti_qspi_exec_mem_op,
> > +};
> > +
> >  static int ti_qspi_start_transfer_one(struct spi_master *master,
> >  		struct spi_message *m)
> >  {
> > @@ -673,6 +730,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
> >  	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
> >  				     SPI_BPW_MASK(8);
> >  	master->spi_flash_read = ti_qspi_spi_flash_read;
> > +	master->mem_ops = &ti_qspi_mem_ops;
> >  
> >  	if (!of_property_read_u32(np, "num-cs", &num_cs))
> >  		master->num_chipselect = num_cs;
> > @@ -785,6 +843,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
> >  				 PTR_ERR(qspi->mmap_base));
> >  			qspi->mmap_base = NULL;
> >  			master->spi_flash_read = NULL;
> > +			master->mem_ops = NULL;
> >  		}
> >  	}
> >  	qspi->mmap_enabled = false;  
> 
> 
> 



-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-11 17:18           ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-11 17:18 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Yogesh Gaur, Vignesh R, Kamal Dasu, Peter Pan,
	Frieder Schrempf, Rafał Miłecki, Sourav Poddar

On Sun, 11 Feb 2018 16:17:06 +0100
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> Hi Boris,
> 
> On Tue,  6 Feb 2018 00:21:18 +0100, Boris Brezillon
> <boris.brezillon@bootlin.com> wrote:
> 
> > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> > 
> > The spi_mem interface is meant to replace the spi_flash_read() one.
> > Implement the ->exec_op() method so that we can smoothly get rid of the
> > old interface.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > ---
> >  drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
> >  1 file changed, 72 insertions(+), 13 deletions(-)
> > 
> > diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> > index c24d9b45a27c..40cac3ef6cc9 100644
> > --- a/drivers/spi/spi-ti-qspi.c
> > +++ b/drivers/spi/spi-ti-qspi.c
> > @@ -434,12 +434,10 @@ static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst,
> >  	return 0;
> >  }
> >  
> > -static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi,
> > -				     struct spi_flash_read_message *msg)
> > +static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi, loff_t offs,
> > +				     void *to, size_t readsize)
> >  {
> > -	size_t readsize = msg->len;
> > -	void *to = msg->buf;
> > -	dma_addr_t dma_src = qspi->mmap_phys_base + msg->from;
> > +	dma_addr_t dma_src = qspi->mmap_phys_base + offs;
> >  	int ret = 0;
> >  
> >  	/*
> > @@ -507,13 +505,14 @@ static void ti_qspi_disable_memory_map(struct spi_device *spi)
> >  	qspi->mmap_enabled = false;
> >  }
> >  
> > -static void ti_qspi_setup_mmap_read(struct spi_device *spi,
> > -				    struct spi_flash_read_message *msg)
> > +static void ti_qspi_setup_mmap_read(struct spi_device *spi, u8 opcode,
> > +				    u8 data_nbits, u8 addr_width,
> > +				    u8 dummy_bytes)
> >  {
> >  	struct ti_qspi  *qspi = spi_master_get_devdata(spi->master);
> > -	u32 memval = msg->read_opcode;
> > +	u32 memval = opcode;
> >  
> > -	switch (msg->data_nbits) {
> > +	switch (data_nbits) {
> >  	case SPI_NBITS_QUAD:
> >  		memval |= QSPI_SETUP_RD_QUAD;
> >  		break;
> > @@ -524,8 +523,8 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi,
> >  		memval |= QSPI_SETUP_RD_NORMAL;
> >  		break;
> >  	}
> > -	memval |= ((msg->addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
> > -		   msg->dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
> > +	memval |= ((addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
> > +		   dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
> >  	ti_qspi_write(qspi, memval,
> >  		      QSPI_SPI_SETUP_REG(spi->chip_select));
> >  }
> > @@ -546,13 +545,15 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
> >  
> >  	if (!qspi->mmap_enabled)
> >  		ti_qspi_enable_memory_map(spi);
> > -	ti_qspi_setup_mmap_read(spi, msg);
> > +	ti_qspi_setup_mmap_read(spi, msg->read_opcode, msg->data_nbits,
> > +				msg->addr_width, msg->dummy_bytes);
> >  
> >  	if (qspi->rx_chan) {
> >  		if (msg->cur_msg_mapped)
> >  			ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from);
> >  		else
> > -			ret = ti_qspi_dma_bounce_buffer(qspi, msg);
> > +			ret = ti_qspi_dma_bounce_buffer(qspi, msg->from,
> > +							msg->buf, msg->len);
> >  		if (ret)
> >  			goto err_unlock;
> >  	} else {
> > @@ -566,6 +567,62 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
> >  	return ret;
> >  }
> >  
> > +static int ti_qspi_exec_mem_op(struct spi_mem *mem,
> > +			       const struct spi_mem_op *op)
> > +{
> > +	struct ti_qspi *qspi = spi_master_get_devdata(mem->spi->master);
> > +	int i, ret = 0;
> > +	u32 from = 0;
> > +
> > +	/* Only optimize read path. */
> > +	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
> > +	    !op->addr.nbytes || op->addr.nbytes > 4)
> > +		return -ENOTSUPP;
> > +
> > +	for (i = 0; i < op->addr.nbytes; i++) {
> > +		from <<= 8;
> > +		from |= op->addr.buf[i];
> > +	}
> > +
> > +	/* Address exceeds MMIO window size, fall back to regular mode. */  
> 
> I don't understand how do you fall back to regular mode?

If you look at spi_mem_exec_op() you'll see that if the controller
->exec_op() returns -ENOTSUPP, the function will try to execute the
operation using the regular spi_sync() API. I'll try to make it clearer
in my next iteration.

> Moreover if the
> purpose of adding this function is to remove spi_flash_read().

Sorry, I don't get that one. Yes, the spi_mem_ops interface is here to
replace the ->spi_flash_xx() one, and that's exactly what I'm doing
here: porting the existing implementation to the new interface, keeping
the exact same limitations (only read path is optimized, and the request
has to fall in the iomem range mapped by the driver).

> 
> > +	if (from > 0x4000000)

Oops! Looks like I forgot to update the code to store the qspi_mmap
resource size in ti_qspi struct and use it here to detect whether
mmap access is allowed or not. Will fix that in v2.

> > +		return -ENOTSUPP;
> > +
> > +	mutex_lock(&qspi->list_lock);
> > +
> > +	if (!qspi->mmap_enabled)
> > +		ti_qspi_enable_memory_map(mem->spi);
> > +	ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
> > +				op->addr.nbytes, op->dummy.nbytes);
> > +
> > +	if (qspi->rx_chan) {
> > +		struct sg_table sgt;
> > +
> > +		if (!virt_addr_valid(op->data.buf.in) &&
> > +		    !spi_controller_dma_map_mem_op_data(mem->spi->master, op,
> > +							&sgt)) {
> > +			ret = ti_qspi_dma_xfer_sg(qspi, sgt, from);
> > +			spi_controller_dma_unmap_mem_op_data(mem->spi->master,
> > +							     op, &sgt);
> > +		} else {
> > +			ret = ti_qspi_dma_bounce_buffer(qspi, from,
> > +							op->data.buf.in,
> > +							op->data.nbytes);
> > +		}
> > +	} else {
> > +		memcpy_fromio(op->data.buf.in, qspi->mmap_base + from,
> > +			      op->data.nbytes);
> > +	}
> > +
> > +	mutex_unlock(&qspi->list_lock);
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> > +	.exec_op = ti_qspi_exec_mem_op,
> > +};
> > +
> >  static int ti_qspi_start_transfer_one(struct spi_master *master,
> >  		struct spi_message *m)
> >  {
> > @@ -673,6 +730,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
> >  	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
> >  				     SPI_BPW_MASK(8);
> >  	master->spi_flash_read = ti_qspi_spi_flash_read;
> > +	master->mem_ops = &ti_qspi_mem_ops;
> >  
> >  	if (!of_property_read_u32(np, "num-cs", &num_cs))
> >  		master->num_chipselect = num_cs;
> > @@ -785,6 +843,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
> >  				 PTR_ERR(qspi->mmap_base));
> >  			qspi->mmap_base = NULL;
> >  			master->spi_flash_read = NULL;
> > +			master->mem_ops = NULL;
> >  		}
> >  	}
> >  	qspi->mmap_enabled = false;  
> 
> 
> 



-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-11 17:18           ` Boris Brezillon
@ 2018-02-12  7:54             ` Miquel Raynal
  -1 siblings, 0 replies; 92+ messages in thread
From: Miquel Raynal @ 2018-02-12  7:54 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Yogesh Gaur, Vignesh R,
	Kamal Dasu, Peter Pan, Frieder Schrempf, Rafał Miłecki,
	Sourav Poddar

Hi Boris,


> > > @@ -566,6 +567,62 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
> > >  	return ret;
> > >  }
> > >  
> > > +static int ti_qspi_exec_mem_op(struct spi_mem *mem,
> > > +			       const struct spi_mem_op *op)
> > > +{
> > > +	struct ti_qspi *qspi = spi_master_get_devdata(mem->spi->master);
> > > +	int i, ret = 0;
> > > +	u32 from = 0;
> > > +
> > > +	/* Only optimize read path. */
> > > +	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
> > > +	    !op->addr.nbytes || op->addr.nbytes > 4)
> > > +		return -ENOTSUPP;
> > > +
> > > +	for (i = 0; i < op->addr.nbytes; i++) {
> > > +		from <<= 8;
> > > +		from |= op->addr.buf[i];
> > > +	}
> > > +
> > > +	/* Address exceeds MMIO window size, fall back to regular mode. */    
> > 
> > I don't understand how do you fall back to regular mode?  
> 
> If you look at spi_mem_exec_op() you'll see that if the controller
> ->exec_op() returns -ENOTSUPP, the function will try to execute the  
> operation using the regular spi_sync() API. I'll try to make it clearer
> in my next iteration.

Ok, I mixed the functions in my head and this answers the below
question as well.

> 
> > Moreover if the
> > purpose of adding this function is to remove spi_flash_read().  
> 
> Sorry, I don't get that one. Yes, the spi_mem_ops interface is here to
> replace the ->spi_flash_xx() one, and that's exactly what I'm doing
> here: porting the existing implementation to the new interface, keeping
> the exact same limitations (only read path is optimized, and the request
> has to fall in the iomem range mapped by the driver).
> 

Thanks,
Miquèl

-- 
Miquel Raynal, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-12  7:54             ` Miquel Raynal
  0 siblings, 0 replies; 92+ messages in thread
From: Miquel Raynal @ 2018-02-12  7:54 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Yogesh Gaur, Vignesh R, Kamal Dasu, Peter Pan,
	Frieder Schrempf, Rafał Miłecki, Sourav Poddar

Hi Boris,


> > > @@ -566,6 +567,62 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
> > >  	return ret;
> > >  }
> > >  
> > > +static int ti_qspi_exec_mem_op(struct spi_mem *mem,
> > > +			       const struct spi_mem_op *op)
> > > +{
> > > +	struct ti_qspi *qspi = spi_master_get_devdata(mem->spi->master);
> > > +	int i, ret = 0;
> > > +	u32 from = 0;
> > > +
> > > +	/* Only optimize read path. */
> > > +	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
> > > +	    !op->addr.nbytes || op->addr.nbytes > 4)
> > > +		return -ENOTSUPP;
> > > +
> > > +	for (i = 0; i < op->addr.nbytes; i++) {
> > > +		from <<= 8;
> > > +		from |= op->addr.buf[i];
> > > +	}
> > > +
> > > +	/* Address exceeds MMIO window size, fall back to regular mode. */    
> > 
> > I don't understand how do you fall back to regular mode?  
> 
> If you look at spi_mem_exec_op() you'll see that if the controller
> ->exec_op() returns -ENOTSUPP, the function will try to execute the  
> operation using the regular spi_sync() API. I'll try to make it clearer
> in my next iteration.

Ok, I mixed the functions in my head and this answers the below
question as well.

> 
> > Moreover if the
> > purpose of adding this function is to remove spi_flash_read().  
> 
> Sorry, I don't get that one. Yes, the spi_mem_ops interface is here to
> replace the ->spi_flash_xx() one, and that's exactly what I'm doing
> here: porting the existing implementation to the new interface, keeping
> the exact same limitations (only read path is optimized, and the request
> has to fall in the iomem range mapped by the driver).
> 

Thanks,
Miquèl

-- 
Miquel Raynal, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-05 23:21     ` Boris Brezillon
@ 2018-02-12 11:43         ` Vignesh R
  -1 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-12 11:43 UTC (permalink / raw)
  To: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu



On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> 
> The spi_mem interface is meant to replace the spi_flash_read() one.
> Implement the ->exec_op() method so that we can smoothly get rid of the
> old interface.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> ---
>  drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 72 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> index c24d9b45a27c..40cac3ef6cc9 100644
> --- a/drivers/spi/spi-ti-qspi.c
> +++ b/drivers/spi/spi-ti-qspi.c

[...]

> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> +	.exec_op = ti_qspi_exec_mem_op,

       .supports_op = ti_qspi_supports_mem_op,

Its required as per spi_controller_check_ops() in Patch 1/6

> +};
> +
>  static int ti_qspi_start_transfer_one(struct spi_master *master,
>  		struct spi_message *m)
>  {
> @@ -673,6 +730,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
>  	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
>  				     SPI_BPW_MASK(8);
>  	master->spi_flash_read = ti_qspi_spi_flash_read;
> +	master->mem_ops = &ti_qspi_mem_ops;
>  
>  	if (!of_property_read_u32(np, "num-cs", &num_cs))
>  		master->num_chipselect = num_cs;
> @@ -785,6 +843,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
>  				 PTR_ERR(qspi->mmap_base));
>  			qspi->mmap_base = NULL;
>  			master->spi_flash_read = NULL;
> +			master->mem_ops = NULL;
>  		}
>  	}
>  	qspi->mmap_enabled = false;
> 

-- 
Regards
Vignesh
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-12 11:43         ` Vignesh R
  0 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-12 11:43 UTC (permalink / raw)
  To: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen, linux-mtd,
	Mark Brown, linux-spi
  Cc: Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu



On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> 
> The spi_mem interface is meant to replace the spi_flash_read() one.
> Implement the ->exec_op() method so that we can smoothly get rid of the
> old interface.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> ---
>  drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 72 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> index c24d9b45a27c..40cac3ef6cc9 100644
> --- a/drivers/spi/spi-ti-qspi.c
> +++ b/drivers/spi/spi-ti-qspi.c

[...]

> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> +	.exec_op = ti_qspi_exec_mem_op,

       .supports_op = ti_qspi_supports_mem_op,

Its required as per spi_controller_check_ops() in Patch 1/6

> +};
> +
>  static int ti_qspi_start_transfer_one(struct spi_master *master,
>  		struct spi_message *m)
>  {
> @@ -673,6 +730,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
>  	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
>  				     SPI_BPW_MASK(8);
>  	master->spi_flash_read = ti_qspi_spi_flash_read;
> +	master->mem_ops = &ti_qspi_mem_ops;
>  
>  	if (!of_property_read_u32(np, "num-cs", &num_cs))
>  		master->num_chipselect = num_cs;
> @@ -785,6 +843,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
>  				 PTR_ERR(qspi->mmap_base));
>  			qspi->mmap_base = NULL;
>  			master->spi_flash_read = NULL;
> +			master->mem_ops = NULL;
>  		}
>  	}
>  	qspi->mmap_enabled = false;
> 

-- 
Regards
Vignesh

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

* Re: [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API
  2018-02-05 23:21     ` Boris Brezillon
@ 2018-02-12 11:44         ` Vignesh R
  -1 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-12 11:44 UTC (permalink / raw)
  To: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu



On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> 
> The spi_mem_xxx() API has been introduced to replace the
> spi_flash_read() one. Make use of it so we can get rid of
> spi_flash_read().
> 
> Note that using spi_mem_xx() also simplifies the code because this API
> takes care of using the regular spi_sync() interface when the optimized
> ->mem_ops interface is not implemented by the controller.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> ---
[...]
>  /*
> @@ -138,92 +121,42 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
>  			   u_char *buf)
>  {
>  	struct m25p *flash = nor->priv;
> -	struct spi_device *spi = flash->spi;
> -	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
> -	struct spi_transfer t[3];
> -	struct spi_message m;
> -	unsigned int dummy = nor->read_dummy;
> -	ssize_t ret;
> -	int cmd_sz;
> +	u8 addrs[4];
> +	struct spi_mem_op op =
> +			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
> +				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
> +				   SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
> +				   SPI_MEM_OP_DATA_OUT(len, buf, 1));

					^^^^ SPI_MEM_OP_DATA_IN

[...]

-- 
Regards
Vignesh
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API
@ 2018-02-12 11:44         ` Vignesh R
  0 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-12 11:44 UTC (permalink / raw)
  To: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen, linux-mtd,
	Mark Brown, linux-spi
  Cc: Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu



On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> 
> The spi_mem_xxx() API has been introduced to replace the
> spi_flash_read() one. Make use of it so we can get rid of
> spi_flash_read().
> 
> Note that using spi_mem_xx() also simplifies the code because this API
> takes care of using the regular spi_sync() interface when the optimized
> ->mem_ops interface is not implemented by the controller.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> ---
[...]
>  /*
> @@ -138,92 +121,42 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
>  			   u_char *buf)
>  {
>  	struct m25p *flash = nor->priv;
> -	struct spi_device *spi = flash->spi;
> -	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
> -	struct spi_transfer t[3];
> -	struct spi_message m;
> -	unsigned int dummy = nor->read_dummy;
> -	ssize_t ret;
> -	int cmd_sz;
> +	u8 addrs[4];
> +	struct spi_mem_op op =
> +			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
> +				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
> +				   SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
> +				   SPI_MEM_OP_DATA_OUT(len, buf, 1));

					^^^^ SPI_MEM_OP_DATA_IN

[...]

-- 
Regards
Vignesh

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-05 23:21     ` Boris Brezillon
@ 2018-02-12 11:50         ` Vignesh R
  -1 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-12 11:50 UTC (permalink / raw)
  To: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

Hi,

On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> 
> Some controllers are exposing high-level interfaces to access various
> kind of SPI memories. Unfortunately they do not fit in the current
> spi_controller model and usually have drivers placed in
> drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> memories in general.
> 
> This is an attempt at defining a SPI memory interface which works for
> all kinds of SPI memories (NORs, NANDs, SRAMs).

Wouldn't it be better if spi_mem* implementations were in separate file?
drivers/spi/spi.c is 3.5K+ lines and is bit difficult to follow.


-- 
Regards
Vignesh
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-02-12 11:50         ` Vignesh R
  0 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-12 11:50 UTC (permalink / raw)
  To: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen, linux-mtd,
	Mark Brown, linux-spi
  Cc: Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

Hi,

On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> 
> Some controllers are exposing high-level interfaces to access various
> kind of SPI memories. Unfortunately they do not fit in the current
> spi_controller model and usually have drivers placed in
> drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> memories in general.
> 
> This is an attempt at defining a SPI memory interface which works for
> all kinds of SPI memories (NORs, NANDs, SRAMs).

Wouldn't it be better if spi_mem* implementations were in separate file?
drivers/spi/spi.c is 3.5K+ lines and is bit difficult to follow.


-- 
Regards
Vignesh

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-12 11:50         ` Vignesh R
@ 2018-02-12 12:28             ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-12 12:28 UTC (permalink / raw)
  To: Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Frieder Schrempf,
	Yogesh Gaur, Rafał Miłecki, Kamal Dasu

On Mon, 12 Feb 2018 17:20:46 +0530
Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:

> Hi,
> 
> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> > From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > 
> > Some controllers are exposing high-level interfaces to access various
> > kind of SPI memories. Unfortunately they do not fit in the current
> > spi_controller model and usually have drivers placed in
> > drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> > memories in general.
> > 
> > This is an attempt at defining a SPI memory interface which works for
> > all kinds of SPI memories (NORs, NANDs, SRAMs).  
> 
> Wouldn't it be better if spi_mem* implementations were in separate file?
> drivers/spi/spi.c is 3.5K+ lines and is bit difficult to follow.

No strong opinion about where the spi_mem code should lie, so if Mark
agrees I can move it to a different file and possibly make it optional
with a Kconfig option.


-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-02-12 12:28             ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-12 12:28 UTC (permalink / raw)
  To: Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

On Mon, 12 Feb 2018 17:20:46 +0530
Vignesh R <vigneshr@ti.com> wrote:

> Hi,
> 
> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> > 
> > Some controllers are exposing high-level interfaces to access various
> > kind of SPI memories. Unfortunately they do not fit in the current
> > spi_controller model and usually have drivers placed in
> > drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> > memories in general.
> > 
> > This is an attempt at defining a SPI memory interface which works for
> > all kinds of SPI memories (NORs, NANDs, SRAMs).  
> 
> Wouldn't it be better if spi_mem* implementations were in separate file?
> drivers/spi/spi.c is 3.5K+ lines and is bit difficult to follow.

No strong opinion about where the spi_mem code should lie, so if Mark
agrees I can move it to a different file and possibly make it optional
with a Kconfig option.


-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-12 11:43         ` Vignesh R
@ 2018-02-12 12:31             ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-12 12:31 UTC (permalink / raw)
  To: Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Frieder Schrempf,
	Yogesh Gaur, Rafał Miłecki, Kamal Dasu

On Mon, 12 Feb 2018 17:13:55 +0530
Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:

> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> > From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > 
> > The spi_mem interface is meant to replace the spi_flash_read() one.
> > Implement the ->exec_op() method so that we can smoothly get rid of the
> > old interface.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > ---
> >  drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
> >  1 file changed, 72 insertions(+), 13 deletions(-)
> > 
> > diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> > index c24d9b45a27c..40cac3ef6cc9 100644
> > --- a/drivers/spi/spi-ti-qspi.c
> > +++ b/drivers/spi/spi-ti-qspi.c  
> 
> [...]
> 
> > +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> > +	.exec_op = ti_qspi_exec_mem_op,  
> 
>        .supports_op = ti_qspi_supports_mem_op,
> 
> Its required as per spi_controller_check_ops() in Patch 1/6

->supports_op() is optional, and if it's missing, the core will do the
regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
implementation). This being said, if you think a custom ->supports_op()
implementation is needed for this controller I can add one.

> 
> > +};
> > +
> >  static int ti_qspi_start_transfer_one(struct spi_master *master,
> >  		struct spi_message *m)
> >  {
> > @@ -673,6 +730,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
> >  	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
> >  				     SPI_BPW_MASK(8);
> >  	master->spi_flash_read = ti_qspi_spi_flash_read;
> > +	master->mem_ops = &ti_qspi_mem_ops;
> >  
> >  	if (!of_property_read_u32(np, "num-cs", &num_cs))
> >  		master->num_chipselect = num_cs;
> > @@ -785,6 +843,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
> >  				 PTR_ERR(qspi->mmap_base));
> >  			qspi->mmap_base = NULL;
> >  			master->spi_flash_read = NULL;
> > +			master->mem_ops = NULL;
> >  		}
> >  	}
> >  	qspi->mmap_enabled = false;
> >   
> 



-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-12 12:31             ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-12 12:31 UTC (permalink / raw)
  To: Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

On Mon, 12 Feb 2018 17:13:55 +0530
Vignesh R <vigneshr@ti.com> wrote:

> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> > 
> > The spi_mem interface is meant to replace the spi_flash_read() one.
> > Implement the ->exec_op() method so that we can smoothly get rid of the
> > old interface.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > ---
> >  drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
> >  1 file changed, 72 insertions(+), 13 deletions(-)
> > 
> > diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> > index c24d9b45a27c..40cac3ef6cc9 100644
> > --- a/drivers/spi/spi-ti-qspi.c
> > +++ b/drivers/spi/spi-ti-qspi.c  
> 
> [...]
> 
> > +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> > +	.exec_op = ti_qspi_exec_mem_op,  
> 
>        .supports_op = ti_qspi_supports_mem_op,
> 
> Its required as per spi_controller_check_ops() in Patch 1/6

->supports_op() is optional, and if it's missing, the core will do the
regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
implementation). This being said, if you think a custom ->supports_op()
implementation is needed for this controller I can add one.

> 
> > +};
> > +
> >  static int ti_qspi_start_transfer_one(struct spi_master *master,
> >  		struct spi_message *m)
> >  {
> > @@ -673,6 +730,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
> >  	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
> >  				     SPI_BPW_MASK(8);
> >  	master->spi_flash_read = ti_qspi_spi_flash_read;
> > +	master->mem_ops = &ti_qspi_mem_ops;
> >  
> >  	if (!of_property_read_u32(np, "num-cs", &num_cs))
> >  		master->num_chipselect = num_cs;
> > @@ -785,6 +843,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
> >  				 PTR_ERR(qspi->mmap_base));
> >  			qspi->mmap_base = NULL;
> >  			master->spi_flash_read = NULL;
> > +			master->mem_ops = NULL;
> >  		}
> >  	}
> >  	qspi->mmap_enabled = false;
> >   
> 



-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API
  2018-02-12 11:44         ` Vignesh R
@ 2018-02-12 12:32             ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-12 12:32 UTC (permalink / raw)
  To: Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Frieder Schrempf,
	Yogesh Gaur, Rafał Miłecki, Kamal Dasu

On Mon, 12 Feb 2018 17:14:21 +0530
Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:

> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> > From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > 
> > The spi_mem_xxx() API has been introduced to replace the
> > spi_flash_read() one. Make use of it so we can get rid of
> > spi_flash_read().
> > 
> > Note that using spi_mem_xx() also simplifies the code because this API
> > takes care of using the regular spi_sync() interface when the optimized  
> > ->mem_ops interface is not implemented by the controller.  
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > ---  
> [...]
> >  /*
> > @@ -138,92 +121,42 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
> >  			   u_char *buf)
> >  {
> >  	struct m25p *flash = nor->priv;
> > -	struct spi_device *spi = flash->spi;
> > -	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
> > -	struct spi_transfer t[3];
> > -	struct spi_message m;
> > -	unsigned int dummy = nor->read_dummy;
> > -	ssize_t ret;
> > -	int cmd_sz;
> > +	u8 addrs[4];
> > +	struct spi_mem_op op =
> > +			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
> > +				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
> > +				   SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
> > +				   SPI_MEM_OP_DATA_OUT(len, buf, 1));  
> 
> 					^^^^ SPI_MEM_OP_DATA_IN

Nice catch! I'll fix that in v2.

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API
@ 2018-02-12 12:32             ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-12 12:32 UTC (permalink / raw)
  To: Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

On Mon, 12 Feb 2018 17:14:21 +0530
Vignesh R <vigneshr@ti.com> wrote:

> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> > 
> > The spi_mem_xxx() API has been introduced to replace the
> > spi_flash_read() one. Make use of it so we can get rid of
> > spi_flash_read().
> > 
> > Note that using spi_mem_xx() also simplifies the code because this API
> > takes care of using the regular spi_sync() interface when the optimized  
> > ->mem_ops interface is not implemented by the controller.  
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > ---  
> [...]
> >  /*
> > @@ -138,92 +121,42 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
> >  			   u_char *buf)
> >  {
> >  	struct m25p *flash = nor->priv;
> > -	struct spi_device *spi = flash->spi;
> > -	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
> > -	struct spi_transfer t[3];
> > -	struct spi_message m;
> > -	unsigned int dummy = nor->read_dummy;
> > -	ssize_t ret;
> > -	int cmd_sz;
> > +	u8 addrs[4];
> > +	struct spi_mem_op op =
> > +			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
> > +				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
> > +				   SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
> > +				   SPI_MEM_OP_DATA_OUT(len, buf, 1));  
> 
> 					^^^^ SPI_MEM_OP_DATA_IN

Nice catch! I'll fix that in v2.

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-12 12:31             ` Boris Brezillon
@ 2018-02-12 16:00               ` Vignesh R
  -1 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-12 16:00 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Frieder Schrempf,
	Yogesh Gaur, Rafał Miłecki, Kamal Dasu

On 12-Feb-18 6:01 PM, Boris Brezillon wrote:
> On Mon, 12 Feb 2018 17:13:55 +0530
> Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
> 
>> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
>> > From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>> > 
>> > The spi_mem interface is meant to replace the spi_flash_read() one.
>> > Implement the ->exec_op() method so that we can smoothly get rid of the
>> > old interface.
>> > 
>> > Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>> > ---
>> >  drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
>> >  1 file changed, 72 insertions(+), 13 deletions(-)
>> > 
>> > diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
>> > index c24d9b45a27c..40cac3ef6cc9 100644
>> > --- a/drivers/spi/spi-ti-qspi.c
>> > +++ b/drivers/spi/spi-ti-qspi.c  
>> 
>> [...]
>> 
>> > +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
>> > +   .exec_op = ti_qspi_exec_mem_op,  
>> 
>>        .supports_op = ti_qspi_supports_mem_op,
>> 
>> Its required as per spi_controller_check_ops() in Patch 1/6
> 
> ->supports_op() is optional, and if it's missing, the core will do the
> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
> implementation). 

You might have overlooked spi_controller_check_ops() from Patch 1/6:
+static int spi_controller_check_ops(struct spi_controller *ctlr)
+{
+	/*
+	 * The controller can implement only the high-level SPI-memory
+	 * operations if it does not support regular SPI transfers.
+	 */
+	if (ctlr->mem_ops) {
+		if (!ctlr->mem_ops->supports_op ||
+		    !ctlr->mem_ops->exec_op)
+			return -EINVAL;
+	} else if (!ctlr->transfer && !ctlr->transfer_one &&
+		   !ctlr->transfer_one_message) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+

So if ->supports_op() is not populated by SPI controller driver, then
driver probe fails with -EINVAL. This is what I observed on my TI
hardware when testing this patch series.

> This being said, if you think a custom ->supports_op()
> implementation is needed for this controller I can add one.
> 

spi_mem_supports_op() should suffice for now if above issue is fixed.

Regards
Vignesh

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-12 16:00               ` Vignesh R
  0 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-12 16:00 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

On 12-Feb-18 6:01 PM, Boris Brezillon wrote:
> On Mon, 12 Feb 2018 17:13:55 +0530
> Vignesh R <vigneshr@ti.com> wrote:
> 
>> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
>> > From: Boris Brezillon <boris.brezillon@free-electrons.com>
>> > 
>> > The spi_mem interface is meant to replace the spi_flash_read() one.
>> > Implement the ->exec_op() method so that we can smoothly get rid of the
>> > old interface.
>> > 
>> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
>> > ---
>> >  drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
>> >  1 file changed, 72 insertions(+), 13 deletions(-)
>> > 
>> > diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
>> > index c24d9b45a27c..40cac3ef6cc9 100644
>> > --- a/drivers/spi/spi-ti-qspi.c
>> > +++ b/drivers/spi/spi-ti-qspi.c  
>> 
>> [...]
>> 
>> > +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
>> > +   .exec_op = ti_qspi_exec_mem_op,  
>> 
>>        .supports_op = ti_qspi_supports_mem_op,
>> 
>> Its required as per spi_controller_check_ops() in Patch 1/6
> 
> ->supports_op() is optional, and if it's missing, the core will do the
> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
> implementation). 

You might have overlooked spi_controller_check_ops() from Patch 1/6:
+static int spi_controller_check_ops(struct spi_controller *ctlr)
+{
+	/*
+	 * The controller can implement only the high-level SPI-memory
+	 * operations if it does not support regular SPI transfers.
+	 */
+	if (ctlr->mem_ops) {
+		if (!ctlr->mem_ops->supports_op ||
+		    !ctlr->mem_ops->exec_op)
+			return -EINVAL;
+	} else if (!ctlr->transfer && !ctlr->transfer_one &&
+		   !ctlr->transfer_one_message) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+

So if ->supports_op() is not populated by SPI controller driver, then
driver probe fails with -EINVAL. This is what I observed on my TI
hardware when testing this patch series.

> This being said, if you think a custom ->supports_op()
> implementation is needed for this controller I can add one.
> 

spi_mem_supports_op() should suffice for now if above issue is fixed.

Regards
Vignesh

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-12 16:00               ` Vignesh R
@ 2018-02-12 16:08                   ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-12 16:08 UTC (permalink / raw)
  To: Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Frieder Schrempf,
	Yogesh Gaur, Rafał Miłecki, Kamal Dasu

On Mon, 12 Feb 2018 21:30:09 +0530
Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:

> On 12-Feb-18 6:01 PM, Boris Brezillon wrote:
> > On Mon, 12 Feb 2018 17:13:55 +0530
> > Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
> >   
> >> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:  
> >> > From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> >> > 
> >> > The spi_mem interface is meant to replace the spi_flash_read() one.
> >> > Implement the ->exec_op() method so that we can smoothly get rid of the
> >> > old interface.
> >> > 
> >> > Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> >> > ---
> >> >  drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
> >> >  1 file changed, 72 insertions(+), 13 deletions(-)
> >> > 
> >> > diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> >> > index c24d9b45a27c..40cac3ef6cc9 100644
> >> > --- a/drivers/spi/spi-ti-qspi.c
> >> > +++ b/drivers/spi/spi-ti-qspi.c    
> >> 
> >> [...]
> >>   
> >> > +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> >> > +   .exec_op = ti_qspi_exec_mem_op,    
> >> 
> >>        .supports_op = ti_qspi_supports_mem_op,
> >> 
> >> Its required as per spi_controller_check_ops() in Patch 1/6  
> >   
> > ->supports_op() is optional, and if it's missing, the core will do the  
> > regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
> > implementation).   
> 
> You might have overlooked spi_controller_check_ops() from Patch 1/6:
> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> +{
> +	/*
> +	 * The controller can implement only the high-level SPI-memory
> +	 * operations if it does not support regular SPI transfers.
> +	 */
> +	if (ctlr->mem_ops) {
> +		if (!ctlr->mem_ops->supports_op ||
> +		    !ctlr->mem_ops->exec_op)
> +			return -EINVAL;
> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> +		   !ctlr->transfer_one_message) {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> 
> So if ->supports_op() is not populated by SPI controller driver, then
> driver probe fails with -EINVAL. This is what I observed on my TI
> hardware when testing this patch series.

Correct. Then I should fix spi_controller_check_ops() to allow empty
ctlr->mem_ops->supports_op.

> 
> > This being said, if you think a custom ->supports_op()
> > implementation is needed for this controller I can add one.
> >   
> 
> spi_mem_supports_op() should suffice for now if above issue is fixed.

Cool. IIUC, you tested the series on a TI SoC. Does it work as
expected? Do you see any perf regressions?

Regards,

Boris


-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-12 16:08                   ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-12 16:08 UTC (permalink / raw)
  To: Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

On Mon, 12 Feb 2018 21:30:09 +0530
Vignesh R <vigneshr@ti.com> wrote:

> On 12-Feb-18 6:01 PM, Boris Brezillon wrote:
> > On Mon, 12 Feb 2018 17:13:55 +0530
> > Vignesh R <vigneshr@ti.com> wrote:
> >   
> >> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:  
> >> > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> >> > 
> >> > The spi_mem interface is meant to replace the spi_flash_read() one.
> >> > Implement the ->exec_op() method so that we can smoothly get rid of the
> >> > old interface.
> >> > 
> >> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> >> > ---
> >> >  drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
> >> >  1 file changed, 72 insertions(+), 13 deletions(-)
> >> > 
> >> > diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> >> > index c24d9b45a27c..40cac3ef6cc9 100644
> >> > --- a/drivers/spi/spi-ti-qspi.c
> >> > +++ b/drivers/spi/spi-ti-qspi.c    
> >> 
> >> [...]
> >>   
> >> > +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> >> > +   .exec_op = ti_qspi_exec_mem_op,    
> >> 
> >>        .supports_op = ti_qspi_supports_mem_op,
> >> 
> >> Its required as per spi_controller_check_ops() in Patch 1/6  
> >   
> > ->supports_op() is optional, and if it's missing, the core will do the  
> > regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
> > implementation).   
> 
> You might have overlooked spi_controller_check_ops() from Patch 1/6:
> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> +{
> +	/*
> +	 * The controller can implement only the high-level SPI-memory
> +	 * operations if it does not support regular SPI transfers.
> +	 */
> +	if (ctlr->mem_ops) {
> +		if (!ctlr->mem_ops->supports_op ||
> +		    !ctlr->mem_ops->exec_op)
> +			return -EINVAL;
> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> +		   !ctlr->transfer_one_message) {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> 
> So if ->supports_op() is not populated by SPI controller driver, then
> driver probe fails with -EINVAL. This is what I observed on my TI
> hardware when testing this patch series.

Correct. Then I should fix spi_controller_check_ops() to allow empty
ctlr->mem_ops->supports_op.

> 
> > This being said, if you think a custom ->supports_op()
> > implementation is needed for this controller I can add one.
> >   
> 
> spi_mem_supports_op() should suffice for now if above issue is fixed.

Cool. IIUC, you tested the series on a TI SoC. Does it work as
expected? Do you see any perf regressions?

Regards,

Boris


-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-12 16:08                   ` Boris Brezillon
@ 2018-02-14 16:25                     ` Vignesh R
  -1 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-14 16:25 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Frieder Schrempf,
	Yogesh Gaur, Rafał Miłecki, Kamal Dasu



On 12-Feb-18 9:38 PM, Boris Brezillon wrote:
> On Mon, 12 Feb 2018 21:30:09 +0530
> Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
> 
>> On 12-Feb-18 6:01 PM, Boris Brezillon wrote:
>>> On Mon, 12 Feb 2018 17:13:55 +0530
>>> Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
>>>   
>>>> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:  
>>>>> From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>>>>>
>>>>> The spi_mem interface is meant to replace the spi_flash_read() one.
>>>>> Implement the ->exec_op() method so that we can smoothly get rid of the
>>>>> old interface.
>>>>>
>>>>> Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>>>>> ---
>>>>>   drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
>>>>>   1 file changed, 72 insertions(+), 13 deletions(-)
>>>>>
>>>>> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
>>>>> index c24d9b45a27c..40cac3ef6cc9 100644
>>>>> --- a/drivers/spi/spi-ti-qspi.c
>>>>> +++ b/drivers/spi/spi-ti-qspi.c    
>>>>
>>>> [...]
>>>>   
>>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
>>>>> +   .exec_op = ti_qspi_exec_mem_op,    
>>>>
>>>>         .supports_op = ti_qspi_supports_mem_op,
>>>>
>>>> Its required as per spi_controller_check_ops() in Patch 1/6  
>>>   
>>> ->supports_op() is optional, and if it's missing, the core will do the  
>>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
>>> implementation).   
>>
>> You might have overlooked spi_controller_check_ops() from Patch 1/6:
>> +static int spi_controller_check_ops(struct spi_controller *ctlr)
>> +{
>> +	/*
>> +	 * The controller can implement only the high-level SPI-memory
>> +	 * operations if it does not support regular SPI transfers.
>> +	 */
>> +	if (ctlr->mem_ops) {
>> +		if (!ctlr->mem_ops->supports_op ||
>> +		    !ctlr->mem_ops->exec_op)
>> +			return -EINVAL;
>> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
>> +		   !ctlr->transfer_one_message) {
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>>
>> So if ->supports_op() is not populated by SPI controller driver, then
>> driver probe fails with -EINVAL. This is what I observed on my TI
>> hardware when testing this patch series.
> 
> Correct. Then I should fix spi_controller_check_ops() to allow empty
> ctlr->mem_ops->supports_op.
> 
>>
>>> This being said, if you think a custom ->supports_op()
>>> implementation is needed for this controller I can add one.
>>>   
>>
>> spi_mem_supports_op() should suffice for now if above issue is fixed.
> 
> Cool. IIUC, you tested the series on a TI SoC. Does it work as
> expected? Do you see any perf regressions?
> 

I am planning to collect throughput numbers with this series for TI
QSPI. I don't think there would be noticeable degradation.
But, it would be interesting to test for a driver thats now under
drivers/mtd/spi-nor moved to drivers/spi and see if added overhead of
m25p80 layer + spi core has any impact.
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-14 16:25                     ` Vignesh R
  0 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-14 16:25 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu



On 12-Feb-18 9:38 PM, Boris Brezillon wrote:
> On Mon, 12 Feb 2018 21:30:09 +0530
> Vignesh R <vigneshr@ti.com> wrote:
> 
>> On 12-Feb-18 6:01 PM, Boris Brezillon wrote:
>>> On Mon, 12 Feb 2018 17:13:55 +0530
>>> Vignesh R <vigneshr@ti.com> wrote:
>>>   
>>>> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:  
>>>>> From: Boris Brezillon <boris.brezillon@free-electrons.com>
>>>>>
>>>>> The spi_mem interface is meant to replace the spi_flash_read() one.
>>>>> Implement the ->exec_op() method so that we can smoothly get rid of the
>>>>> old interface.
>>>>>
>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
>>>>> ---
>>>>>   drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
>>>>>   1 file changed, 72 insertions(+), 13 deletions(-)
>>>>>
>>>>> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
>>>>> index c24d9b45a27c..40cac3ef6cc9 100644
>>>>> --- a/drivers/spi/spi-ti-qspi.c
>>>>> +++ b/drivers/spi/spi-ti-qspi.c    
>>>>
>>>> [...]
>>>>   
>>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
>>>>> +   .exec_op = ti_qspi_exec_mem_op,    
>>>>
>>>>         .supports_op = ti_qspi_supports_mem_op,
>>>>
>>>> Its required as per spi_controller_check_ops() in Patch 1/6  
>>>   
>>> ->supports_op() is optional, and if it's missing, the core will do the  
>>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
>>> implementation).   
>>
>> You might have overlooked spi_controller_check_ops() from Patch 1/6:
>> +static int spi_controller_check_ops(struct spi_controller *ctlr)
>> +{
>> +	/*
>> +	 * The controller can implement only the high-level SPI-memory
>> +	 * operations if it does not support regular SPI transfers.
>> +	 */
>> +	if (ctlr->mem_ops) {
>> +		if (!ctlr->mem_ops->supports_op ||
>> +		    !ctlr->mem_ops->exec_op)
>> +			return -EINVAL;
>> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
>> +		   !ctlr->transfer_one_message) {
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>>
>> So if ->supports_op() is not populated by SPI controller driver, then
>> driver probe fails with -EINVAL. This is what I observed on my TI
>> hardware when testing this patch series.
> 
> Correct. Then I should fix spi_controller_check_ops() to allow empty
> ctlr->mem_ops->supports_op.
> 
>>
>>> This being said, if you think a custom ->supports_op()
>>> implementation is needed for this controller I can add one.
>>>   
>>
>> spi_mem_supports_op() should suffice for now if above issue is fixed.
> 
> Cool. IIUC, you tested the series on a TI SoC. Does it work as
> expected? Do you see any perf regressions?
> 

I am planning to collect throughput numbers with this series for TI
QSPI. I don't think there would be noticeable degradation.
But, it would be interesting to test for a driver thats now under
drivers/mtd/spi-nor moved to drivers/spi and see if added overhead of
m25p80 layer + spi core has any impact.

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-14 16:25                     ` Vignesh R
@ 2018-02-14 19:09                         ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-14 19:09 UTC (permalink / raw)
  To: Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Frieder Schrempf,
	Yogesh Gaur, Rafał Miłecki, Kamal Dasu

On Wed, 14 Feb 2018 21:55:10 +0530
Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:

> On 12-Feb-18 9:38 PM, Boris Brezillon wrote:
> > On Mon, 12 Feb 2018 21:30:09 +0530
> > Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
> >   
> >> On 12-Feb-18 6:01 PM, Boris Brezillon wrote:  
> >>> On Mon, 12 Feb 2018 17:13:55 +0530
> >>> Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
> >>>     
> >>>> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:    
> >>>>> From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> >>>>>
> >>>>> The spi_mem interface is meant to replace the spi_flash_read() one.
> >>>>> Implement the ->exec_op() method so that we can smoothly get rid of the
> >>>>> old interface.
> >>>>>
> >>>>> Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> >>>>> ---
> >>>>>   drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
> >>>>>   1 file changed, 72 insertions(+), 13 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> >>>>> index c24d9b45a27c..40cac3ef6cc9 100644
> >>>>> --- a/drivers/spi/spi-ti-qspi.c
> >>>>> +++ b/drivers/spi/spi-ti-qspi.c      
> >>>>
> >>>> [...]
> >>>>     
> >>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> >>>>> +   .exec_op = ti_qspi_exec_mem_op,      
> >>>>
> >>>>         .supports_op = ti_qspi_supports_mem_op,
> >>>>
> >>>> Its required as per spi_controller_check_ops() in Patch 1/6    
> >>>     
> >>> ->supports_op() is optional, and if it's missing, the core will do the    
> >>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
> >>> implementation).     
> >>
> >> You might have overlooked spi_controller_check_ops() from Patch 1/6:
> >> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> >> +{
> >> +	/*
> >> +	 * The controller can implement only the high-level SPI-memory
> >> +	 * operations if it does not support regular SPI transfers.
> >> +	 */
> >> +	if (ctlr->mem_ops) {
> >> +		if (!ctlr->mem_ops->supports_op ||
> >> +		    !ctlr->mem_ops->exec_op)
> >> +			return -EINVAL;
> >> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> >> +		   !ctlr->transfer_one_message) {
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >>
> >> So if ->supports_op() is not populated by SPI controller driver, then
> >> driver probe fails with -EINVAL. This is what I observed on my TI
> >> hardware when testing this patch series.  
> > 
> > Correct. Then I should fix spi_controller_check_ops() to allow empty
> > ctlr->mem_ops->supports_op.
> >   
> >>  
> >>> This being said, if you think a custom ->supports_op()
> >>> implementation is needed for this controller I can add one.
> >>>     
> >>
> >> spi_mem_supports_op() should suffice for now if above issue is fixed.  
> > 
> > Cool. IIUC, you tested the series on a TI SoC. Does it work as
> > expected? Do you see any perf regressions?
> >   
> 
> I am planning to collect throughput numbers with this series for TI
> QSPI. I don't think there would be noticeable degradation.

Ok.

> But, it would be interesting to test for a driver thats now under
> drivers/mtd/spi-nor moved to drivers/spi and see if added overhead of
> m25p80 layer + spi core has any impact.

I'm working with Frieder on the fsl-qspi rework, so we should have
numbers soon.

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-14 19:09                         ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-14 19:09 UTC (permalink / raw)
  To: Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

On Wed, 14 Feb 2018 21:55:10 +0530
Vignesh R <vigneshr@ti.com> wrote:

> On 12-Feb-18 9:38 PM, Boris Brezillon wrote:
> > On Mon, 12 Feb 2018 21:30:09 +0530
> > Vignesh R <vigneshr@ti.com> wrote:
> >   
> >> On 12-Feb-18 6:01 PM, Boris Brezillon wrote:  
> >>> On Mon, 12 Feb 2018 17:13:55 +0530
> >>> Vignesh R <vigneshr@ti.com> wrote:
> >>>     
> >>>> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:    
> >>>>> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> >>>>>
> >>>>> The spi_mem interface is meant to replace the spi_flash_read() one.
> >>>>> Implement the ->exec_op() method so that we can smoothly get rid of the
> >>>>> old interface.
> >>>>>
> >>>>> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> >>>>> ---
> >>>>>   drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
> >>>>>   1 file changed, 72 insertions(+), 13 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> >>>>> index c24d9b45a27c..40cac3ef6cc9 100644
> >>>>> --- a/drivers/spi/spi-ti-qspi.c
> >>>>> +++ b/drivers/spi/spi-ti-qspi.c      
> >>>>
> >>>> [...]
> >>>>     
> >>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> >>>>> +   .exec_op = ti_qspi_exec_mem_op,      
> >>>>
> >>>>         .supports_op = ti_qspi_supports_mem_op,
> >>>>
> >>>> Its required as per spi_controller_check_ops() in Patch 1/6    
> >>>     
> >>> ->supports_op() is optional, and if it's missing, the core will do the    
> >>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
> >>> implementation).     
> >>
> >> You might have overlooked spi_controller_check_ops() from Patch 1/6:
> >> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> >> +{
> >> +	/*
> >> +	 * The controller can implement only the high-level SPI-memory
> >> +	 * operations if it does not support regular SPI transfers.
> >> +	 */
> >> +	if (ctlr->mem_ops) {
> >> +		if (!ctlr->mem_ops->supports_op ||
> >> +		    !ctlr->mem_ops->exec_op)
> >> +			return -EINVAL;
> >> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> >> +		   !ctlr->transfer_one_message) {
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >>
> >> So if ->supports_op() is not populated by SPI controller driver, then
> >> driver probe fails with -EINVAL. This is what I observed on my TI
> >> hardware when testing this patch series.  
> > 
> > Correct. Then I should fix spi_controller_check_ops() to allow empty
> > ctlr->mem_ops->supports_op.
> >   
> >>  
> >>> This being said, if you think a custom ->supports_op()
> >>> implementation is needed for this controller I can add one.
> >>>     
> >>
> >> spi_mem_supports_op() should suffice for now if above issue is fixed.  
> > 
> > Cool. IIUC, you tested the series on a TI SoC. Does it work as
> > expected? Do you see any perf regressions?
> >   
> 
> I am planning to collect throughput numbers with this series for TI
> QSPI. I don't think there would be noticeable degradation.

Ok.

> But, it would be interesting to test for a driver thats now under
> drivers/mtd/spi-nor moved to drivers/spi and see if added overhead of
> m25p80 layer + spi core has any impact.

I'm working with Frieder on the fsl-qspi rework, so we should have
numbers soon.

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-14 19:09                         ` Boris Brezillon
@ 2018-02-14 20:44                           ` Schrempf Frieder
  -1 siblings, 0 replies; 92+ messages in thread
From: Schrempf Frieder @ 2018-02-14 20:44 UTC (permalink / raw)
  To: Boris Brezillon, Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

Hi Boris, hi Vignesh,

On 14.02.2018 20:09, Boris Brezillon wrote:
> On Wed, 14 Feb 2018 21:55:10 +0530
> Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
> 
>> On 12-Feb-18 9:38 PM, Boris Brezillon wrote:
>>> On Mon, 12 Feb 2018 21:30:09 +0530
>>> Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
>>>    
>>>> On 12-Feb-18 6:01 PM, Boris Brezillon wrote:
>>>>> On Mon, 12 Feb 2018 17:13:55 +0530
>>>>> Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
>>>>>      
>>>>>> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
>>>>>>> From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>>>>>>>
>>>>>>> The spi_mem interface is meant to replace the spi_flash_read() one.
>>>>>>> Implement the ->exec_op() method so that we can smoothly get rid of the
>>>>>>> old interface.
>>>>>>>
>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>>>>>>> ---
>>>>>>>    drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
>>>>>>>    1 file changed, 72 insertions(+), 13 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
>>>>>>> index c24d9b45a27c..40cac3ef6cc9 100644
>>>>>>> --- a/drivers/spi/spi-ti-qspi.c
>>>>>>> +++ b/drivers/spi/spi-ti-qspi.c
>>>>>>
>>>>>> [...]
>>>>>>      
>>>>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
>>>>>>> +   .exec_op = ti_qspi_exec_mem_op,
>>>>>>
>>>>>>          .supports_op = ti_qspi_supports_mem_op,
>>>>>>
>>>>>> Its required as per spi_controller_check_ops() in Patch 1/6
>>>>>      
>>>>> ->supports_op() is optional, and if it's missing, the core will do the
>>>>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
>>>>> implementation).
>>>>
>>>> You might have overlooked spi_controller_check_ops() from Patch 1/6:
>>>> +static int spi_controller_check_ops(struct spi_controller *ctlr)
>>>> +{
>>>> +	/*
>>>> +	 * The controller can implement only the high-level SPI-memory
>>>> +	 * operations if it does not support regular SPI transfers.
>>>> +	 */
>>>> +	if (ctlr->mem_ops) {
>>>> +		if (!ctlr->mem_ops->supports_op ||
>>>> +		    !ctlr->mem_ops->exec_op)
>>>> +			return -EINVAL;
>>>> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
>>>> +		   !ctlr->transfer_one_message) {
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>>
>>>> So if ->supports_op() is not populated by SPI controller driver, then
>>>> driver probe fails with -EINVAL. This is what I observed on my TI
>>>> hardware when testing this patch series.
>>>
>>> Correct. Then I should fix spi_controller_check_ops() to allow empty
>>> ctlr->mem_ops->supports_op.
>>>    
>>>>   
>>>>> This being said, if you think a custom ->supports_op()
>>>>> implementation is needed for this controller I can add one.
>>>>>      
>>>>
>>>> spi_mem_supports_op() should suffice for now if above issue is fixed.
>>>
>>> Cool. IIUC, you tested the series on a TI SoC. Does it work as
>>> expected? Do you see any perf regressions?
>>>    
>>
>> I am planning to collect throughput numbers with this series for TI
>> QSPI. I don't think there would be noticeable degradation.
> 
> Ok.
> 
>> But, it would be interesting to test for a driver thats now under
>> drivers/mtd/spi-nor moved to drivers/spi and see if added overhead of
>> m25p80 layer + spi core has any impact.
> 
> I'm working with Frieder on the fsl-qspi rework, so we should have
> numbers soon.

I made a speed comparison between fsl-quadspi.c and Boris' 
spi-fsl-qspi.c using a Micron MT25QL512 64MB NOR: [1]

It seems like the spi-mem-based driver is slower up to about 40%.

I had to remove USE_FSR, as FSR read/write doesn't work with both drivers:

-       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | 
USE_FSR | SPI_NOR_QUAD_READ) },
+       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | 
SPI_NOR_QUAD_READ) },

For the spi-mem driver I set spi-tx-bus-width = <1> to match with 
fsl-quadspi.c (does not use quad write).
My dts looks like this for both cases:

&qspi {
     pinctrl-names = "default";
     pinctrl-0 = <&pinctrl_qspi>;
     status = "okay";

     flash0: n25q512ax3@0 {
         #address-cells = <1>;
         #size-cells = <1>;
         compatible = "micron,n25q512ax3", "jedec,spi-nor";
         spi-max-frequency = <108000000>;
         spi-rx-bus-width = <4>;
         spi-tx-bus-width = <1>;
         reg = <0>;
     };
};

Regards,

Frieder

[1]: https://paste.ee/p/dc9KM
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-14 20:44                           ` Schrempf Frieder
  0 siblings, 0 replies; 92+ messages in thread
From: Schrempf Frieder @ 2018-02-14 20:44 UTC (permalink / raw)
  To: Boris Brezillon, Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Peter Pan, Yogesh Gaur, Rafał Miłecki,
	Kamal Dasu

Hi Boris, hi Vignesh,

On 14.02.2018 20:09, Boris Brezillon wrote:
> On Wed, 14 Feb 2018 21:55:10 +0530
> Vignesh R <vigneshr@ti.com> wrote:
> 
>> On 12-Feb-18 9:38 PM, Boris Brezillon wrote:
>>> On Mon, 12 Feb 2018 21:30:09 +0530
>>> Vignesh R <vigneshr@ti.com> wrote:
>>>    
>>>> On 12-Feb-18 6:01 PM, Boris Brezillon wrote:
>>>>> On Mon, 12 Feb 2018 17:13:55 +0530
>>>>> Vignesh R <vigneshr@ti.com> wrote:
>>>>>      
>>>>>> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
>>>>>>> From: Boris Brezillon <boris.brezillon@free-electrons.com>
>>>>>>>
>>>>>>> The spi_mem interface is meant to replace the spi_flash_read() one.
>>>>>>> Implement the ->exec_op() method so that we can smoothly get rid of the
>>>>>>> old interface.
>>>>>>>
>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
>>>>>>> ---
>>>>>>>    drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
>>>>>>>    1 file changed, 72 insertions(+), 13 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
>>>>>>> index c24d9b45a27c..40cac3ef6cc9 100644
>>>>>>> --- a/drivers/spi/spi-ti-qspi.c
>>>>>>> +++ b/drivers/spi/spi-ti-qspi.c
>>>>>>
>>>>>> [...]
>>>>>>      
>>>>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
>>>>>>> +   .exec_op = ti_qspi_exec_mem_op,
>>>>>>
>>>>>>          .supports_op = ti_qspi_supports_mem_op,
>>>>>>
>>>>>> Its required as per spi_controller_check_ops() in Patch 1/6
>>>>>      
>>>>> ->supports_op() is optional, and if it's missing, the core will do the
>>>>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
>>>>> implementation).
>>>>
>>>> You might have overlooked spi_controller_check_ops() from Patch 1/6:
>>>> +static int spi_controller_check_ops(struct spi_controller *ctlr)
>>>> +{
>>>> +	/*
>>>> +	 * The controller can implement only the high-level SPI-memory
>>>> +	 * operations if it does not support regular SPI transfers.
>>>> +	 */
>>>> +	if (ctlr->mem_ops) {
>>>> +		if (!ctlr->mem_ops->supports_op ||
>>>> +		    !ctlr->mem_ops->exec_op)
>>>> +			return -EINVAL;
>>>> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
>>>> +		   !ctlr->transfer_one_message) {
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>>
>>>> So if ->supports_op() is not populated by SPI controller driver, then
>>>> driver probe fails with -EINVAL. This is what I observed on my TI
>>>> hardware when testing this patch series.
>>>
>>> Correct. Then I should fix spi_controller_check_ops() to allow empty
>>> ctlr->mem_ops->supports_op.
>>>    
>>>>   
>>>>> This being said, if you think a custom ->supports_op()
>>>>> implementation is needed for this controller I can add one.
>>>>>      
>>>>
>>>> spi_mem_supports_op() should suffice for now if above issue is fixed.
>>>
>>> Cool. IIUC, you tested the series on a TI SoC. Does it work as
>>> expected? Do you see any perf regressions?
>>>    
>>
>> I am planning to collect throughput numbers with this series for TI
>> QSPI. I don't think there would be noticeable degradation.
> 
> Ok.
> 
>> But, it would be interesting to test for a driver thats now under
>> drivers/mtd/spi-nor moved to drivers/spi and see if added overhead of
>> m25p80 layer + spi core has any impact.
> 
> I'm working with Frieder on the fsl-qspi rework, so we should have
> numbers soon.

I made a speed comparison between fsl-quadspi.c and Boris' 
spi-fsl-qspi.c using a Micron MT25QL512 64MB NOR: [1]

It seems like the spi-mem-based driver is slower up to about 40%.

I had to remove USE_FSR, as FSR read/write doesn't work with both drivers:

-       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | 
USE_FSR | SPI_NOR_QUAD_READ) },
+       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | 
SPI_NOR_QUAD_READ) },

For the spi-mem driver I set spi-tx-bus-width = <1> to match with 
fsl-quadspi.c (does not use quad write).
My dts looks like this for both cases:

&qspi {
     pinctrl-names = "default";
     pinctrl-0 = <&pinctrl_qspi>;
     status = "okay";

     flash0: n25q512ax3@0 {
         #address-cells = <1>;
         #size-cells = <1>;
         compatible = "micron,n25q512ax3", "jedec,spi-nor";
         spi-max-frequency = <108000000>;
         spi-rx-bus-width = <4>;
         spi-tx-bus-width = <1>;
         reg = <0>;
     };
};

Regards,

Frieder

[1]: https://paste.ee/p/dc9KM

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-14 20:44                           ` Schrempf Frieder
@ 2018-02-14 21:00                               ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-14 21:00 UTC (permalink / raw)
  To: Schrempf Frieder
  Cc: Vignesh R, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

On Wed, 14 Feb 2018 20:44:20 +0000
Schrempf Frieder <frieder.schrempf-wPoT/lNZgHizQB+pC5nmwQ@public.gmane.org> wrote:

> Hi Boris, hi Vignesh,
> 
> On 14.02.2018 20:09, Boris Brezillon wrote:
> > On Wed, 14 Feb 2018 21:55:10 +0530
> > Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
> >   
> >> On 12-Feb-18 9:38 PM, Boris Brezillon wrote:  
> >>> On Mon, 12 Feb 2018 21:30:09 +0530
> >>> Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
> >>>      
> >>>> On 12-Feb-18 6:01 PM, Boris Brezillon wrote:  
> >>>>> On Mon, 12 Feb 2018 17:13:55 +0530
> >>>>> Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
> >>>>>        
> >>>>>> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:  
> >>>>>>> From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> >>>>>>>
> >>>>>>> The spi_mem interface is meant to replace the spi_flash_read() one.
> >>>>>>> Implement the ->exec_op() method so that we can smoothly get rid of the
> >>>>>>> old interface.
> >>>>>>>
> >>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> >>>>>>> ---
> >>>>>>>    drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
> >>>>>>>    1 file changed, 72 insertions(+), 13 deletions(-)
> >>>>>>>
> >>>>>>> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> >>>>>>> index c24d9b45a27c..40cac3ef6cc9 100644
> >>>>>>> --- a/drivers/spi/spi-ti-qspi.c
> >>>>>>> +++ b/drivers/spi/spi-ti-qspi.c  
> >>>>>>
> >>>>>> [...]
> >>>>>>        
> >>>>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> >>>>>>> +   .exec_op = ti_qspi_exec_mem_op,  
> >>>>>>
> >>>>>>          .supports_op = ti_qspi_supports_mem_op,
> >>>>>>
> >>>>>> Its required as per spi_controller_check_ops() in Patch 1/6  
> >>>>>        
> >>>>> ->supports_op() is optional, and if it's missing, the core will do the  
> >>>>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
> >>>>> implementation).  
> >>>>
> >>>> You might have overlooked spi_controller_check_ops() from Patch 1/6:
> >>>> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> >>>> +{
> >>>> +	/*
> >>>> +	 * The controller can implement only the high-level SPI-memory
> >>>> +	 * operations if it does not support regular SPI transfers.
> >>>> +	 */
> >>>> +	if (ctlr->mem_ops) {
> >>>> +		if (!ctlr->mem_ops->supports_op ||
> >>>> +		    !ctlr->mem_ops->exec_op)
> >>>> +			return -EINVAL;
> >>>> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> >>>> +		   !ctlr->transfer_one_message) {
> >>>> +		return -EINVAL;
> >>>> +	}
> >>>> +
> >>>> +	return 0;
> >>>> +}
> >>>> +
> >>>>
> >>>> So if ->supports_op() is not populated by SPI controller driver, then
> >>>> driver probe fails with -EINVAL. This is what I observed on my TI
> >>>> hardware when testing this patch series.  
> >>>
> >>> Correct. Then I should fix spi_controller_check_ops() to allow empty
> >>> ctlr->mem_ops->supports_op.
> >>>      
> >>>>     
> >>>>> This being said, if you think a custom ->supports_op()
> >>>>> implementation is needed for this controller I can add one.
> >>>>>        
> >>>>
> >>>> spi_mem_supports_op() should suffice for now if above issue is fixed.  
> >>>
> >>> Cool. IIUC, you tested the series on a TI SoC. Does it work as
> >>> expected? Do you see any perf regressions?
> >>>      
> >>
> >> I am planning to collect throughput numbers with this series for TI
> >> QSPI. I don't think there would be noticeable degradation.  
> > 
> > Ok.
> >   
> >> But, it would be interesting to test for a driver thats now under
> >> drivers/mtd/spi-nor moved to drivers/spi and see if added overhead of
> >> m25p80 layer + spi core has any impact.  
> > 
> > I'm working with Frieder on the fsl-qspi rework, so we should have
> > numbers soon.  
> 
> I made a speed comparison between fsl-quadspi.c and Boris' 
> spi-fsl-qspi.c using a Micron MT25QL512 64MB NOR: [1]
> 
> It seems like the spi-mem-based driver is slower up to about 40%.

Ouch, not good! Are you sure the clk is running at the same freq (you
can check /sys/kernel/debug/clk/clk_summary)?

I guess the read path is optimized by some kind of pre-fetching (that's
particularly true for the 'read eraseblock' test where we see a 50%
gain with the old driver) which can't be done with the new driver (at
least in its current state). What I'm more surprised about is the
difference in the write speed. 

> 
> I had to remove USE_FSR, as FSR read/write doesn't work with both drivers:
> 
> -       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | 
> USE_FSR | SPI_NOR_QUAD_READ) },
> +       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | 
> SPI_NOR_QUAD_READ) },
> 
> For the spi-mem driver I set spi-tx-bus-width = <1> to match with 
> fsl-quadspi.c (does not use quad write).
> My dts looks like this for both cases:
> 
> &qspi {
>      pinctrl-names = "default";
>      pinctrl-0 = <&pinctrl_qspi>;
>      status = "okay";
> 
>      flash0: n25q512ax3@0 {
>          #address-cells = <1>;
>          #size-cells = <1>;
>          compatible = "micron,n25q512ax3", "jedec,spi-nor";
>          spi-max-frequency = <108000000>;
>          spi-rx-bus-width = <4>;
>          spi-tx-bus-width = <1>;
>          reg = <0>;
>      };
> };
> 
> Regards,
> 
> Frieder
> 
> [1]: https://paste.ee/p/dc9KM



-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-14 21:00                               ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-14 21:00 UTC (permalink / raw)
  To: Schrempf Frieder
  Cc: Vignesh R, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen, linux-mtd,
	Mark Brown, linux-spi, Peter Pan, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

On Wed, 14 Feb 2018 20:44:20 +0000
Schrempf Frieder <frieder.schrempf@exceet.de> wrote:

> Hi Boris, hi Vignesh,
> 
> On 14.02.2018 20:09, Boris Brezillon wrote:
> > On Wed, 14 Feb 2018 21:55:10 +0530
> > Vignesh R <vigneshr@ti.com> wrote:
> >   
> >> On 12-Feb-18 9:38 PM, Boris Brezillon wrote:  
> >>> On Mon, 12 Feb 2018 21:30:09 +0530
> >>> Vignesh R <vigneshr@ti.com> wrote:
> >>>      
> >>>> On 12-Feb-18 6:01 PM, Boris Brezillon wrote:  
> >>>>> On Mon, 12 Feb 2018 17:13:55 +0530
> >>>>> Vignesh R <vigneshr@ti.com> wrote:
> >>>>>        
> >>>>>> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:  
> >>>>>>> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> >>>>>>>
> >>>>>>> The spi_mem interface is meant to replace the spi_flash_read() one.
> >>>>>>> Implement the ->exec_op() method so that we can smoothly get rid of the
> >>>>>>> old interface.
> >>>>>>>
> >>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> >>>>>>> ---
> >>>>>>>    drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
> >>>>>>>    1 file changed, 72 insertions(+), 13 deletions(-)
> >>>>>>>
> >>>>>>> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
> >>>>>>> index c24d9b45a27c..40cac3ef6cc9 100644
> >>>>>>> --- a/drivers/spi/spi-ti-qspi.c
> >>>>>>> +++ b/drivers/spi/spi-ti-qspi.c  
> >>>>>>
> >>>>>> [...]
> >>>>>>        
> >>>>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> >>>>>>> +   .exec_op = ti_qspi_exec_mem_op,  
> >>>>>>
> >>>>>>          .supports_op = ti_qspi_supports_mem_op,
> >>>>>>
> >>>>>> Its required as per spi_controller_check_ops() in Patch 1/6  
> >>>>>        
> >>>>> ->supports_op() is optional, and if it's missing, the core will do the  
> >>>>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
> >>>>> implementation).  
> >>>>
> >>>> You might have overlooked spi_controller_check_ops() from Patch 1/6:
> >>>> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> >>>> +{
> >>>> +	/*
> >>>> +	 * The controller can implement only the high-level SPI-memory
> >>>> +	 * operations if it does not support regular SPI transfers.
> >>>> +	 */
> >>>> +	if (ctlr->mem_ops) {
> >>>> +		if (!ctlr->mem_ops->supports_op ||
> >>>> +		    !ctlr->mem_ops->exec_op)
> >>>> +			return -EINVAL;
> >>>> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> >>>> +		   !ctlr->transfer_one_message) {
> >>>> +		return -EINVAL;
> >>>> +	}
> >>>> +
> >>>> +	return 0;
> >>>> +}
> >>>> +
> >>>>
> >>>> So if ->supports_op() is not populated by SPI controller driver, then
> >>>> driver probe fails with -EINVAL. This is what I observed on my TI
> >>>> hardware when testing this patch series.  
> >>>
> >>> Correct. Then I should fix spi_controller_check_ops() to allow empty
> >>> ctlr->mem_ops->supports_op.
> >>>      
> >>>>     
> >>>>> This being said, if you think a custom ->supports_op()
> >>>>> implementation is needed for this controller I can add one.
> >>>>>        
> >>>>
> >>>> spi_mem_supports_op() should suffice for now if above issue is fixed.  
> >>>
> >>> Cool. IIUC, you tested the series on a TI SoC. Does it work as
> >>> expected? Do you see any perf regressions?
> >>>      
> >>
> >> I am planning to collect throughput numbers with this series for TI
> >> QSPI. I don't think there would be noticeable degradation.  
> > 
> > Ok.
> >   
> >> But, it would be interesting to test for a driver thats now under
> >> drivers/mtd/spi-nor moved to drivers/spi and see if added overhead of
> >> m25p80 layer + spi core has any impact.  
> > 
> > I'm working with Frieder on the fsl-qspi rework, so we should have
> > numbers soon.  
> 
> I made a speed comparison between fsl-quadspi.c and Boris' 
> spi-fsl-qspi.c using a Micron MT25QL512 64MB NOR: [1]
> 
> It seems like the spi-mem-based driver is slower up to about 40%.

Ouch, not good! Are you sure the clk is running at the same freq (you
can check /sys/kernel/debug/clk/clk_summary)?

I guess the read path is optimized by some kind of pre-fetching (that's
particularly true for the 'read eraseblock' test where we see a 50%
gain with the old driver) which can't be done with the new driver (at
least in its current state). What I'm more surprised about is the
difference in the write speed. 

> 
> I had to remove USE_FSR, as FSR read/write doesn't work with both drivers:
> 
> -       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | 
> USE_FSR | SPI_NOR_QUAD_READ) },
> +       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | 
> SPI_NOR_QUAD_READ) },
> 
> For the spi-mem driver I set spi-tx-bus-width = <1> to match with 
> fsl-quadspi.c (does not use quad write).
> My dts looks like this for both cases:
> 
> &qspi {
>      pinctrl-names = "default";
>      pinctrl-0 = <&pinctrl_qspi>;
>      status = "okay";
> 
>      flash0: n25q512ax3@0 {
>          #address-cells = <1>;
>          #size-cells = <1>;
>          compatible = "micron,n25q512ax3", "jedec,spi-nor";
>          spi-max-frequency = <108000000>;
>          spi-rx-bus-width = <4>;
>          spi-tx-bus-width = <1>;
>          reg = <0>;
>      };
> };
> 
> Regards,
> 
> Frieder
> 
> [1]: https://paste.ee/p/dc9KM



-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-14 21:00                               ` Boris Brezillon
@ 2018-02-15 16:38                                 ` Schrempf Frieder
  -1 siblings, 0 replies; 92+ messages in thread
From: Schrempf Frieder @ 2018-02-15 16:38 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Vignesh R, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

Hi,

On 14.02.2018 22:00, Boris Brezillon wrote:
> On Wed, 14 Feb 2018 20:44:20 +0000
> Schrempf Frieder <frieder.schrempf-wPoT/lNZgHizQB+pC5nmwQ@public.gmane.org> wrote:
> 
>> Hi Boris, hi Vignesh,
>>
>> On 14.02.2018 20:09, Boris Brezillon wrote:
>>> On Wed, 14 Feb 2018 21:55:10 +0530
>>> Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
>>>    
>>>> On 12-Feb-18 9:38 PM, Boris Brezillon wrote:
>>>>> On Mon, 12 Feb 2018 21:30:09 +0530
>>>>> Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
>>>>>       
>>>>>> On 12-Feb-18 6:01 PM, Boris Brezillon wrote:
>>>>>>> On Mon, 12 Feb 2018 17:13:55 +0530
>>>>>>> Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:
>>>>>>>         
>>>>>>>> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
>>>>>>>>> From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>>>>>>>>>
>>>>>>>>> The spi_mem interface is meant to replace the spi_flash_read() one.
>>>>>>>>> Implement the ->exec_op() method so that we can smoothly get rid of the
>>>>>>>>> old interface.
>>>>>>>>>
>>>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>>>>>>>>> ---
>>>>>>>>>     drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
>>>>>>>>>     1 file changed, 72 insertions(+), 13 deletions(-)
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
>>>>>>>>> index c24d9b45a27c..40cac3ef6cc9 100644
>>>>>>>>> --- a/drivers/spi/spi-ti-qspi.c
>>>>>>>>> +++ b/drivers/spi/spi-ti-qspi.c
>>>>>>>>
>>>>>>>> [...]
>>>>>>>>         
>>>>>>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
>>>>>>>>> +   .exec_op = ti_qspi_exec_mem_op,
>>>>>>>>
>>>>>>>>           .supports_op = ti_qspi_supports_mem_op,
>>>>>>>>
>>>>>>>> Its required as per spi_controller_check_ops() in Patch 1/6
>>>>>>>         
>>>>>>> ->supports_op() is optional, and if it's missing, the core will do the
>>>>>>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
>>>>>>> implementation).
>>>>>>
>>>>>> You might have overlooked spi_controller_check_ops() from Patch 1/6:
>>>>>> +static int spi_controller_check_ops(struct spi_controller *ctlr)
>>>>>> +{
>>>>>> +	/*
>>>>>> +	 * The controller can implement only the high-level SPI-memory
>>>>>> +	 * operations if it does not support regular SPI transfers.
>>>>>> +	 */
>>>>>> +	if (ctlr->mem_ops) {
>>>>>> +		if (!ctlr->mem_ops->supports_op ||
>>>>>> +		    !ctlr->mem_ops->exec_op)
>>>>>> +			return -EINVAL;
>>>>>> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
>>>>>> +		   !ctlr->transfer_one_message) {
>>>>>> +		return -EINVAL;
>>>>>> +	}
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>>
>>>>>> So if ->supports_op() is not populated by SPI controller driver, then
>>>>>> driver probe fails with -EINVAL. This is what I observed on my TI
>>>>>> hardware when testing this patch series.
>>>>>
>>>>> Correct. Then I should fix spi_controller_check_ops() to allow empty
>>>>> ctlr->mem_ops->supports_op.
>>>>>       
>>>>>>      
>>>>>>> This being said, if you think a custom ->supports_op()
>>>>>>> implementation is needed for this controller I can add one.
>>>>>>>         
>>>>>>
>>>>>> spi_mem_supports_op() should suffice for now if above issue is fixed.
>>>>>
>>>>> Cool. IIUC, you tested the series on a TI SoC. Does it work as
>>>>> expected? Do you see any perf regressions?
>>>>>       
>>>>
>>>> I am planning to collect throughput numbers with this series for TI
>>>> QSPI. I don't think there would be noticeable degradation.
>>>
>>> Ok.
>>>    
>>>> But, it would be interesting to test for a driver thats now under
>>>> drivers/mtd/spi-nor moved to drivers/spi and see if added overhead of
>>>> m25p80 layer + spi core has any impact.
>>>
>>> I'm working with Frieder on the fsl-qspi rework, so we should have
>>> numbers soon.
>>
>> I made a speed comparison between fsl-quadspi.c and Boris'
>> spi-fsl-qspi.c using a Micron MT25QL512 64MB NOR: [1]
>>
>> It seems like the spi-mem-based driver is slower up to about 40%.
> 
> Ouch, not good! Are you sure the clk is running at the same freq (you
> can check /sys/kernel/debug/clk/clk_summary)?
> 
> I guess the read path is optimized by some kind of pre-fetching (that's
> particularly true for the 'read eraseblock' test where we see a 50%
> gain with the old driver) which can't be done with the new driver (at
> least in its current state). What I'm more surprised about is the
> difference in the write speed.

So as Boris and I found out, moving the clk_prep_enable/disable() calls 
out of exec_op() improves performance a lot.

The write performance for single eraseblocks is still slower (~12%).
But apart from that the other values are almost equal or even faster.

As Boris tried out with his SPI NAND implementation, an other measure to 
improve performance is to use polling while waiting for the completion 
of a flash operation, as the current implementation with interrupt 
causes "long" scheduling latency delays.

> 
>>
>> I had to remove USE_FSR, as FSR read/write doesn't work with both drivers:
>>
>> -       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K |
>> USE_FSR | SPI_NOR_QUAD_READ) },
>> +       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K |
>> SPI_NOR_QUAD_READ) },
>>
>> For the spi-mem driver I set spi-tx-bus-width = <1> to match with
>> fsl-quadspi.c (does not use quad write).
>> My dts looks like this for both cases:
>>
>> &qspi {
>>       pinctrl-names = "default";
>>       pinctrl-0 = <&pinctrl_qspi>;
>>       status = "okay";
>>
>>       flash0: n25q512ax3@0 {
>>           #address-cells = <1>;
>>           #size-cells = <1>;
>>           compatible = "micron,n25q512ax3", "jedec,spi-nor";
>>           spi-max-frequency = <108000000>;
>>           spi-rx-bus-width = <4>;
>>           spi-tx-bus-width = <1>;
>>           reg = <0>;
>>       };
>> };
>>
>> Regards,
>>
>> Frieder
>>
>> [1]: https://paste.ee/p/dc9KM

Regards,

Frieder

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-15 16:38                                 ` Schrempf Frieder
  0 siblings, 0 replies; 92+ messages in thread
From: Schrempf Frieder @ 2018-02-15 16:38 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Vignesh R, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen, linux-mtd,
	Mark Brown, linux-spi, Peter Pan, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

Hi,

On 14.02.2018 22:00, Boris Brezillon wrote:
> On Wed, 14 Feb 2018 20:44:20 +0000
> Schrempf Frieder <frieder.schrempf@exceet.de> wrote:
> 
>> Hi Boris, hi Vignesh,
>>
>> On 14.02.2018 20:09, Boris Brezillon wrote:
>>> On Wed, 14 Feb 2018 21:55:10 +0530
>>> Vignesh R <vigneshr@ti.com> wrote:
>>>    
>>>> On 12-Feb-18 9:38 PM, Boris Brezillon wrote:
>>>>> On Mon, 12 Feb 2018 21:30:09 +0530
>>>>> Vignesh R <vigneshr@ti.com> wrote:
>>>>>       
>>>>>> On 12-Feb-18 6:01 PM, Boris Brezillon wrote:
>>>>>>> On Mon, 12 Feb 2018 17:13:55 +0530
>>>>>>> Vignesh R <vigneshr@ti.com> wrote:
>>>>>>>         
>>>>>>>> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
>>>>>>>>> From: Boris Brezillon <boris.brezillon@free-electrons.com>
>>>>>>>>>
>>>>>>>>> The spi_mem interface is meant to replace the spi_flash_read() one.
>>>>>>>>> Implement the ->exec_op() method so that we can smoothly get rid of the
>>>>>>>>> old interface.
>>>>>>>>>
>>>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
>>>>>>>>> ---
>>>>>>>>>     drivers/spi/spi-ti-qspi.c | 85 +++++++++++++++++++++++++++++++++++++++--------
>>>>>>>>>     1 file changed, 72 insertions(+), 13 deletions(-)
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
>>>>>>>>> index c24d9b45a27c..40cac3ef6cc9 100644
>>>>>>>>> --- a/drivers/spi/spi-ti-qspi.c
>>>>>>>>> +++ b/drivers/spi/spi-ti-qspi.c
>>>>>>>>
>>>>>>>> [...]
>>>>>>>>         
>>>>>>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
>>>>>>>>> +   .exec_op = ti_qspi_exec_mem_op,
>>>>>>>>
>>>>>>>>           .supports_op = ti_qspi_supports_mem_op,
>>>>>>>>
>>>>>>>> Its required as per spi_controller_check_ops() in Patch 1/6
>>>>>>>         
>>>>>>> ->supports_op() is optional, and if it's missing, the core will do the
>>>>>>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
>>>>>>> implementation).
>>>>>>
>>>>>> You might have overlooked spi_controller_check_ops() from Patch 1/6:
>>>>>> +static int spi_controller_check_ops(struct spi_controller *ctlr)
>>>>>> +{
>>>>>> +	/*
>>>>>> +	 * The controller can implement only the high-level SPI-memory
>>>>>> +	 * operations if it does not support regular SPI transfers.
>>>>>> +	 */
>>>>>> +	if (ctlr->mem_ops) {
>>>>>> +		if (!ctlr->mem_ops->supports_op ||
>>>>>> +		    !ctlr->mem_ops->exec_op)
>>>>>> +			return -EINVAL;
>>>>>> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
>>>>>> +		   !ctlr->transfer_one_message) {
>>>>>> +		return -EINVAL;
>>>>>> +	}
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>>
>>>>>> So if ->supports_op() is not populated by SPI controller driver, then
>>>>>> driver probe fails with -EINVAL. This is what I observed on my TI
>>>>>> hardware when testing this patch series.
>>>>>
>>>>> Correct. Then I should fix spi_controller_check_ops() to allow empty
>>>>> ctlr->mem_ops->supports_op.
>>>>>       
>>>>>>      
>>>>>>> This being said, if you think a custom ->supports_op()
>>>>>>> implementation is needed for this controller I can add one.
>>>>>>>         
>>>>>>
>>>>>> spi_mem_supports_op() should suffice for now if above issue is fixed.
>>>>>
>>>>> Cool. IIUC, you tested the series on a TI SoC. Does it work as
>>>>> expected? Do you see any perf regressions?
>>>>>       
>>>>
>>>> I am planning to collect throughput numbers with this series for TI
>>>> QSPI. I don't think there would be noticeable degradation.
>>>
>>> Ok.
>>>    
>>>> But, it would be interesting to test for a driver thats now under
>>>> drivers/mtd/spi-nor moved to drivers/spi and see if added overhead of
>>>> m25p80 layer + spi core has any impact.
>>>
>>> I'm working with Frieder on the fsl-qspi rework, so we should have
>>> numbers soon.
>>
>> I made a speed comparison between fsl-quadspi.c and Boris'
>> spi-fsl-qspi.c using a Micron MT25QL512 64MB NOR: [1]
>>
>> It seems like the spi-mem-based driver is slower up to about 40%.
> 
> Ouch, not good! Are you sure the clk is running at the same freq (you
> can check /sys/kernel/debug/clk/clk_summary)?
> 
> I guess the read path is optimized by some kind of pre-fetching (that's
> particularly true for the 'read eraseblock' test where we see a 50%
> gain with the old driver) which can't be done with the new driver (at
> least in its current state). What I'm more surprised about is the
> difference in the write speed.

So as Boris and I found out, moving the clk_prep_enable/disable() calls 
out of exec_op() improves performance a lot.

The write performance for single eraseblocks is still slower (~12%).
But apart from that the other values are almost equal or even faster.

As Boris tried out with his SPI NAND implementation, an other measure to 
improve performance is to use polling while waiting for the completion 
of a flash operation, as the current implementation with interrupt 
causes "long" scheduling latency delays.

> 
>>
>> I had to remove USE_FSR, as FSR read/write doesn't work with both drivers:
>>
>> -       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K |
>> USE_FSR | SPI_NOR_QUAD_READ) },
>> +       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K |
>> SPI_NOR_QUAD_READ) },
>>
>> For the spi-mem driver I set spi-tx-bus-width = <1> to match with
>> fsl-quadspi.c (does not use quad write).
>> My dts looks like this for both cases:
>>
>> &qspi {
>>       pinctrl-names = "default";
>>       pinctrl-0 = <&pinctrl_qspi>;
>>       status = "okay";
>>
>>       flash0: n25q512ax3@0 {
>>           #address-cells = <1>;
>>           #size-cells = <1>;
>>           compatible = "micron,n25q512ax3", "jedec,spi-nor";
>>           spi-max-frequency = <108000000>;
>>           spi-rx-bus-width = <4>;
>>           spi-tx-bus-width = <1>;
>>           reg = <0>;
>>       };
>> };
>>
>> Regards,
>>
>> Frieder
>>
>> [1]: https://paste.ee/p/dc9KM

Regards,

Frieder

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

* Re: [RFC PATCH 6/6] spi: Get rid of the spi_flash_read() API
  2018-02-05 23:21     ` Boris Brezillon
@ 2018-02-16 10:21         ` Vignesh R
  -1 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-16 10:21 UTC (permalink / raw)
  To: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

Hi Boris,

On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> 
> This API has been replaced by the spi_mem_xx() one, its only user
> (spi-nor) has been converted to spi_mem_xx() and all SPI controller
> drivers that were implementing the ->spi_flash_xxx() hooks are also
> implementing the spi_mem ones. So we can safely get rid of this API.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> ---

>  drivers/spi/spi-ti-qspi.c  | 41 ---------------------------------

> -static bool ti_qspi_spi_flash_can_dma(struct spi_device *spi,
> -				      struct spi_flash_read_message *msg)
> -{
> -	return virt_addr_valid(msg->buf);
> -}
> -


This causes kernel crash because spi_map_buf() now tries to map
vmalloc'd buffers in LPAE backed memory not accessible by 32bit DMA:

ubiattach -p /dev/mtd9
[  114.482439] Unable to handle kernel paging request at virtual address
bf867000
[  114.489689] pgd = 045b2870
[  114.492403] [bf867000] *pgd=ad22c003, *pmd=00000000
[  114.497309] Internal error: Oops: 2a06 [#1] PREEMPT SMP ARM
[  114.502903] Modules linked in: ecdh_generic extcon_usb_gpio
omap_aes_driver omap_sham omap_des des_generic crypto_engine omap_crypto
[  114.514894] CPU: 1 PID: 1862 Comm: ubiattach Not tainted
4.16.0-rc1-next-20180212-00009-gbaa4bd31694e-dirty #16
[  114.525023] Hardware name: Generic DRA74X (Flattened Device Tree)
[  114.531145] PC is at v7_dma_inv_range+0x30/0x48
[  114.535693] LR is at dma_cache_maint_page+0x110/0x124
[  114.540763] pc : [<c0218fdc>]    lr : [<c02149e0>]    p


We need ->can_dma like interface in spi_controller_mem_ops to prevent
such buffers being mapped for certain SoCs. Or SPI NOR framework needs
to use bounce buffers.


-- 
Regards
Vignesh
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 6/6] spi: Get rid of the spi_flash_read() API
@ 2018-02-16 10:21         ` Vignesh R
  0 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-16 10:21 UTC (permalink / raw)
  To: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen, linux-mtd,
	Mark Brown, linux-spi
  Cc: Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

Hi Boris,

On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> 
> This API has been replaced by the spi_mem_xx() one, its only user
> (spi-nor) has been converted to spi_mem_xx() and all SPI controller
> drivers that were implementing the ->spi_flash_xxx() hooks are also
> implementing the spi_mem ones. So we can safely get rid of this API.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> ---

>  drivers/spi/spi-ti-qspi.c  | 41 ---------------------------------

> -static bool ti_qspi_spi_flash_can_dma(struct spi_device *spi,
> -				      struct spi_flash_read_message *msg)
> -{
> -	return virt_addr_valid(msg->buf);
> -}
> -


This causes kernel crash because spi_map_buf() now tries to map
vmalloc'd buffers in LPAE backed memory not accessible by 32bit DMA:

ubiattach -p /dev/mtd9
[  114.482439] Unable to handle kernel paging request at virtual address
bf867000
[  114.489689] pgd = 045b2870
[  114.492403] [bf867000] *pgd=ad22c003, *pmd=00000000
[  114.497309] Internal error: Oops: 2a06 [#1] PREEMPT SMP ARM
[  114.502903] Modules linked in: ecdh_generic extcon_usb_gpio
omap_aes_driver omap_sham omap_des des_generic crypto_engine omap_crypto
[  114.514894] CPU: 1 PID: 1862 Comm: ubiattach Not tainted
4.16.0-rc1-next-20180212-00009-gbaa4bd31694e-dirty #16
[  114.525023] Hardware name: Generic DRA74X (Flattened Device Tree)
[  114.531145] PC is at v7_dma_inv_range+0x30/0x48
[  114.535693] LR is at dma_cache_maint_page+0x110/0x124
[  114.540763] pc : [<c0218fdc>]    lr : [<c02149e0>]    p


We need ->can_dma like interface in spi_controller_mem_ops to prevent
such buffers being mapped for certain SoCs. Or SPI NOR framework needs
to use bounce buffers.


-- 
Regards
Vignesh

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

* Re: [RFC PATCH 6/6] spi: Get rid of the spi_flash_read() API
  2018-02-16 10:21         ` Vignesh R
@ 2018-02-16 10:24             ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-16 10:24 UTC (permalink / raw)
  To: Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Frieder Schrempf,
	Yogesh Gaur, Rafał Miłecki, Kamal Dasu

On Fri, 16 Feb 2018 15:51:00 +0530
Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:

> Hi Boris,
> 
> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> > From: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > 
> > This API has been replaced by the spi_mem_xx() one, its only user
> > (spi-nor) has been converted to spi_mem_xx() and all SPI controller
> > drivers that were implementing the ->spi_flash_xxx() hooks are also
> > implementing the spi_mem ones. So we can safely get rid of this API.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> > ---  
> 
> >  drivers/spi/spi-ti-qspi.c  | 41 ---------------------------------  
> 
> > -static bool ti_qspi_spi_flash_can_dma(struct spi_device *spi,
> > -				      struct spi_flash_read_message *msg)
> > -{
> > -	return virt_addr_valid(msg->buf);
> > -}
> > -  
> 
> 
> This causes kernel crash because spi_map_buf() now tries to map
> vmalloc'd buffers in LPAE backed memory not accessible by 32bit DMA:
> 
> ubiattach -p /dev/mtd9
> [  114.482439] Unable to handle kernel paging request at virtual address
> bf867000
> [  114.489689] pgd = 045b2870
> [  114.492403] [bf867000] *pgd=ad22c003, *pmd=00000000
> [  114.497309] Internal error: Oops: 2a06 [#1] PREEMPT SMP ARM
> [  114.502903] Modules linked in: ecdh_generic extcon_usb_gpio
> omap_aes_driver omap_sham omap_des des_generic crypto_engine omap_crypto
> [  114.514894] CPU: 1 PID: 1862 Comm: ubiattach Not tainted
> 4.16.0-rc1-next-20180212-00009-gbaa4bd31694e-dirty #16
> [  114.525023] Hardware name: Generic DRA74X (Flattened Device Tree)
> [  114.531145] PC is at v7_dma_inv_range+0x30/0x48
> [  114.535693] LR is at dma_cache_maint_page+0x110/0x124
> [  114.540763] pc : [<c0218fdc>]    lr : [<c02149e0>]    p
> 
> 
> We need ->can_dma like interface in spi_controller_mem_ops to prevent
> such buffers being mapped for certain SoCs. Or SPI NOR framework needs
> to use bounce buffers.
> 
> 

Actually, I'm doing the test directly in ti_qspi_exec_mem_op(), it's
just that my test is wrong :-):

		if (!virt_addr_valid(op->data.buf.in) &&
		    !spi_controller_dma_map_mem_op_data(mem->spi->master,
							op, &sgt)) {

should be

		if (virt_addr_valid(op->data.buf.in) &&
		    !spi_controller_dma_map_mem_op_data(mem->spi->master,
							op, &sgt)) {


-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 6/6] spi: Get rid of the spi_flash_read() API
@ 2018-02-16 10:24             ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-16 10:24 UTC (permalink / raw)
  To: Vignesh R
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu

On Fri, 16 Feb 2018 15:51:00 +0530
Vignesh R <vigneshr@ti.com> wrote:

> Hi Boris,
> 
> On Tuesday 06 February 2018 04:51 AM, Boris Brezillon wrote:
> > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> > 
> > This API has been replaced by the spi_mem_xx() one, its only user
> > (spi-nor) has been converted to spi_mem_xx() and all SPI controller
> > drivers that were implementing the ->spi_flash_xxx() hooks are also
> > implementing the spi_mem ones. So we can safely get rid of this API.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > ---  
> 
> >  drivers/spi/spi-ti-qspi.c  | 41 ---------------------------------  
> 
> > -static bool ti_qspi_spi_flash_can_dma(struct spi_device *spi,
> > -				      struct spi_flash_read_message *msg)
> > -{
> > -	return virt_addr_valid(msg->buf);
> > -}
> > -  
> 
> 
> This causes kernel crash because spi_map_buf() now tries to map
> vmalloc'd buffers in LPAE backed memory not accessible by 32bit DMA:
> 
> ubiattach -p /dev/mtd9
> [  114.482439] Unable to handle kernel paging request at virtual address
> bf867000
> [  114.489689] pgd = 045b2870
> [  114.492403] [bf867000] *pgd=ad22c003, *pmd=00000000
> [  114.497309] Internal error: Oops: 2a06 [#1] PREEMPT SMP ARM
> [  114.502903] Modules linked in: ecdh_generic extcon_usb_gpio
> omap_aes_driver omap_sham omap_des des_generic crypto_engine omap_crypto
> [  114.514894] CPU: 1 PID: 1862 Comm: ubiattach Not tainted
> 4.16.0-rc1-next-20180212-00009-gbaa4bd31694e-dirty #16
> [  114.525023] Hardware name: Generic DRA74X (Flattened Device Tree)
> [  114.531145] PC is at v7_dma_inv_range+0x30/0x48
> [  114.535693] LR is at dma_cache_maint_page+0x110/0x124
> [  114.540763] pc : [<c0218fdc>]    lr : [<c02149e0>]    p
> 
> 
> We need ->can_dma like interface in spi_controller_mem_ops to prevent
> such buffers being mapped for certain SoCs. Or SPI NOR framework needs
> to use bounce buffers.
> 
> 

Actually, I'm doing the test directly in ti_qspi_exec_mem_op(), it's
just that my test is wrong :-):

		if (!virt_addr_valid(op->data.buf.in) &&
		    !spi_controller_dma_map_mem_op_data(mem->spi->master,
							op, &sgt)) {

should be

		if (virt_addr_valid(op->data.buf.in) &&
		    !spi_controller_dma_map_mem_op_data(mem->spi->master,
							op, &sgt)) {


-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-05 23:21     ` Boris Brezillon
@ 2018-02-16 10:25         ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-16 10:25 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

On Tue,  6 Feb 2018 00:21:18 +0100
Boris Brezillon <boris.brezillon-LDxbnhwyfcJBDgjK7y7TUQ@public.gmane.org> wrote:


> @@ -566,6 +567,62 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
>  	return ret;
>  }
>  
> +static int ti_qspi_exec_mem_op(struct spi_mem *mem,
> +			       const struct spi_mem_op *op)
> +{
> +	struct ti_qspi *qspi = spi_master_get_devdata(mem->spi->master);
> +	int i, ret = 0;
> +	u32 from = 0;
> +
> +	/* Only optimize read path. */
> +	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
> +	    !op->addr.nbytes || op->addr.nbytes > 4)
> +		return -ENOTSUPP;
> +
> +	for (i = 0; i < op->addr.nbytes; i++) {
> +		from <<= 8;
> +		from |= op->addr.buf[i];
> +	}
> +
> +	/* Address exceeds MMIO window size, fall back to regular mode. */
> +	if (from > 0x4000000)
> +		return -ENOTSUPP;
> +
> +	mutex_lock(&qspi->list_lock);
> +
> +	if (!qspi->mmap_enabled)
> +		ti_qspi_enable_memory_map(mem->spi);
> +	ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
> +				op->addr.nbytes, op->dummy.nbytes);
> +
> +	if (qspi->rx_chan) {
> +		struct sg_table sgt;
> +
> +		if (!virt_addr_valid(op->data.buf.in) &&
> +		    !spi_controller_dma_map_mem_op_data(mem->spi->master, op,
> +							&sgt)) {

As Vignesh just reported, it should be virt_addr_valid(op->data.buf.in)
and not !virt_addr_valid(op->data.buf.in).

> +			ret = ti_qspi_dma_xfer_sg(qspi, sgt, from);
> +			spi_controller_dma_unmap_mem_op_data(mem->spi->master,
> +							     op, &sgt);
> +		} else {
> +			ret = ti_qspi_dma_bounce_buffer(qspi, from,
> +							op->data.buf.in,
> +							op->data.nbytes);
> +		}
> +	} else {
> +		memcpy_fromio(op->data.buf.in, qspi->mmap_base + from,
> +			      op->data.nbytes);
> +	}
> +
> +	mutex_unlock(&qspi->list_lock);
> +
> +	return ret;
> +}
> +
> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> +	.exec_op = ti_qspi_exec_mem_op,
> +};
> +
>  static int ti_qspi_start_transfer_one(struct spi_master *master,
>  		struct spi_message *m)
>  {
> @@ -673,6 +730,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
>  	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
>  				     SPI_BPW_MASK(8);
>  	master->spi_flash_read = ti_qspi_spi_flash_read;
> +	master->mem_ops = &ti_qspi_mem_ops;
>  
>  	if (!of_property_read_u32(np, "num-cs", &num_cs))
>  		master->num_chipselect = num_cs;
> @@ -785,6 +843,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
>  				 PTR_ERR(qspi->mmap_base));
>  			qspi->mmap_base = NULL;
>  			master->spi_flash_read = NULL;
> +			master->mem_ops = NULL;
>  		}
>  	}
>  	qspi->mmap_enabled = false;



-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-16 10:25         ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-16 10:25 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

On Tue,  6 Feb 2018 00:21:18 +0100
Boris Brezillon <boris.brezillon@bootlin.com> wrote:


> @@ -566,6 +567,62 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi,
>  	return ret;
>  }
>  
> +static int ti_qspi_exec_mem_op(struct spi_mem *mem,
> +			       const struct spi_mem_op *op)
> +{
> +	struct ti_qspi *qspi = spi_master_get_devdata(mem->spi->master);
> +	int i, ret = 0;
> +	u32 from = 0;
> +
> +	/* Only optimize read path. */
> +	if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
> +	    !op->addr.nbytes || op->addr.nbytes > 4)
> +		return -ENOTSUPP;
> +
> +	for (i = 0; i < op->addr.nbytes; i++) {
> +		from <<= 8;
> +		from |= op->addr.buf[i];
> +	}
> +
> +	/* Address exceeds MMIO window size, fall back to regular mode. */
> +	if (from > 0x4000000)
> +		return -ENOTSUPP;
> +
> +	mutex_lock(&qspi->list_lock);
> +
> +	if (!qspi->mmap_enabled)
> +		ti_qspi_enable_memory_map(mem->spi);
> +	ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
> +				op->addr.nbytes, op->dummy.nbytes);
> +
> +	if (qspi->rx_chan) {
> +		struct sg_table sgt;
> +
> +		if (!virt_addr_valid(op->data.buf.in) &&
> +		    !spi_controller_dma_map_mem_op_data(mem->spi->master, op,
> +							&sgt)) {

As Vignesh just reported, it should be virt_addr_valid(op->data.buf.in)
and not !virt_addr_valid(op->data.buf.in).

> +			ret = ti_qspi_dma_xfer_sg(qspi, sgt, from);
> +			spi_controller_dma_unmap_mem_op_data(mem->spi->master,
> +							     op, &sgt);
> +		} else {
> +			ret = ti_qspi_dma_bounce_buffer(qspi, from,
> +							op->data.buf.in,
> +							op->data.nbytes);
> +		}
> +	} else {
> +		memcpy_fromio(op->data.buf.in, qspi->mmap_base + from,
> +			      op->data.nbytes);
> +	}
> +
> +	mutex_unlock(&qspi->list_lock);
> +
> +	return ret;
> +}
> +
> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> +	.exec_op = ti_qspi_exec_mem_op,
> +};
> +
>  static int ti_qspi_start_transfer_one(struct spi_master *master,
>  		struct spi_message *m)
>  {
> @@ -673,6 +730,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
>  	master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
>  				     SPI_BPW_MASK(8);
>  	master->spi_flash_read = ti_qspi_spi_flash_read;
> +	master->mem_ops = &ti_qspi_mem_ops;
>  
>  	if (!of_property_read_u32(np, "num-cs", &num_cs))
>  		master->num_chipselect = num_cs;
> @@ -785,6 +843,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
>  				 PTR_ERR(qspi->mmap_base));
>  			qspi->mmap_base = NULL;
>  			master->spi_flash_read = NULL;
> +			master->mem_ops = NULL;
>  		}
>  	}
>  	qspi->mmap_enabled = false;



-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-14 19:09                         ` Boris Brezillon
@ 2018-02-17 11:01                           ` Vignesh R
  -1 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-17 11:01 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Frieder Schrempf,
	Yogesh Gaur, Rafał Miłecki, Kamal Dasu


[...]
>>>>>>     
>>>>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
>>>>>>> +   .exec_op = ti_qspi_exec_mem_op,      
>>>>>>
>>>>>>         .supports_op = ti_qspi_supports_mem_op,
>>>>>>
>>>>>> Its required as per spi_controller_check_ops() in Patch 1/6    
>>>>>     
>>>>> ->supports_op() is optional, and if it's missing, the core will do the    
>>>>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
>>>>> implementation).     
>>>>
>>>> You might have overlooked spi_controller_check_ops() from Patch 1/6:
>>>> +static int spi_controller_check_ops(struct spi_controller *ctlr)
>>>> +{
>>>> +	/*
>>>> +	 * The controller can implement only the high-level SPI-memory
>>>> +	 * operations if it does not support regular SPI transfers.
>>>> +	 */
>>>> +	if (ctlr->mem_ops) {
>>>> +		if (!ctlr->mem_ops->supports_op ||
>>>> +		    !ctlr->mem_ops->exec_op)
>>>> +			return -EINVAL;
>>>> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
>>>> +		   !ctlr->transfer_one_message) {
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>>
>>>> So if ->supports_op() is not populated by SPI controller driver, then
>>>> driver probe fails with -EINVAL. This is what I observed on my TI
>>>> hardware when testing this patch series.  
>>>
>>> Correct. Then I should fix spi_controller_check_ops() to allow empty
>>> ctlr->mem_ops->supports_op.
>>>   
>>>>  
>>>>> This being said, if you think a custom ->supports_op()
>>>>> implementation is needed for this controller I can add one.
>>>>>     
>>>>
>>>> spi_mem_supports_op() should suffice for now if above issue is fixed.  
>>>
>>> Cool. IIUC, you tested the series on a TI SoC. Does it work as
>>> expected? Do you see any perf regressions?
>>>   

No other functional failures or performance issues so far wrt TI QSPI.
Things look good :)


-- 
Regards
Vignesh
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-17 11:01                           ` Vignesh R
  0 siblings, 0 replies; 92+ messages in thread
From: Vignesh R @ 2018-02-17 11:01 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Peter Pan, Frieder Schrempf, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu


[...]
>>>>>>     
>>>>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
>>>>>>> +   .exec_op = ti_qspi_exec_mem_op,      
>>>>>>
>>>>>>         .supports_op = ti_qspi_supports_mem_op,
>>>>>>
>>>>>> Its required as per spi_controller_check_ops() in Patch 1/6    
>>>>>     
>>>>> ->supports_op() is optional, and if it's missing, the core will do the    
>>>>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
>>>>> implementation).     
>>>>
>>>> You might have overlooked spi_controller_check_ops() from Patch 1/6:
>>>> +static int spi_controller_check_ops(struct spi_controller *ctlr)
>>>> +{
>>>> +	/*
>>>> +	 * The controller can implement only the high-level SPI-memory
>>>> +	 * operations if it does not support regular SPI transfers.
>>>> +	 */
>>>> +	if (ctlr->mem_ops) {
>>>> +		if (!ctlr->mem_ops->supports_op ||
>>>> +		    !ctlr->mem_ops->exec_op)
>>>> +			return -EINVAL;
>>>> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
>>>> +		   !ctlr->transfer_one_message) {
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>>
>>>> So if ->supports_op() is not populated by SPI controller driver, then
>>>> driver probe fails with -EINVAL. This is what I observed on my TI
>>>> hardware when testing this patch series.  
>>>
>>> Correct. Then I should fix spi_controller_check_ops() to allow empty
>>> ctlr->mem_ops->supports_op.
>>>   
>>>>  
>>>>> This being said, if you think a custom ->supports_op()
>>>>> implementation is needed for this controller I can add one.
>>>>>     
>>>>
>>>> spi_mem_supports_op() should suffice for now if above issue is fixed.  
>>>
>>> Cool. IIUC, you tested the series on a TI SoC. Does it work as
>>> expected? Do you see any perf regressions?
>>>   

No other functional failures or performance issues so far wrt TI QSPI.
Things look good :)


-- 
Regards
Vignesh

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
  2018-02-17 11:01                           ` Vignesh R
@ 2018-02-17 21:52                               ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-17 21:52 UTC (permalink / raw)
  To: Vignesh R
  Cc: Boris Brezillon, Yogesh Gaur, Kamal Dasu, Richard Weinberger,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, Peter Pan, Marek Vasut,
	Frieder Schrempf, Mark Brown,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Cyrille Pitchen,
	Rafał Miłecki, Brian Norris, David Woodhouse

On Sat, 17 Feb 2018 16:31:48 +0530
Vignesh R <vigneshr-l0cyMroinI0@public.gmane.org> wrote:

> [...]
> >>>>>>       
> >>>>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> >>>>>>> +   .exec_op = ti_qspi_exec_mem_op,        
> >>>>>>
> >>>>>>         .supports_op = ti_qspi_supports_mem_op,
> >>>>>>
> >>>>>> Its required as per spi_controller_check_ops() in Patch 1/6      
> >>>>>       
> >>>>> ->supports_op() is optional, and if it's missing, the core will do the      
> >>>>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
> >>>>> implementation).       
> >>>>
> >>>> You might have overlooked spi_controller_check_ops() from Patch 1/6:
> >>>> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> >>>> +{
> >>>> +	/*
> >>>> +	 * The controller can implement only the high-level SPI-memory
> >>>> +	 * operations if it does not support regular SPI transfers.
> >>>> +	 */
> >>>> +	if (ctlr->mem_ops) {
> >>>> +		if (!ctlr->mem_ops->supports_op ||
> >>>> +		    !ctlr->mem_ops->exec_op)
> >>>> +			return -EINVAL;
> >>>> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> >>>> +		   !ctlr->transfer_one_message) {
> >>>> +		return -EINVAL;
> >>>> +	}
> >>>> +
> >>>> +	return 0;
> >>>> +}
> >>>> +
> >>>>
> >>>> So if ->supports_op() is not populated by SPI controller driver, then
> >>>> driver probe fails with -EINVAL. This is what I observed on my TI
> >>>> hardware when testing this patch series.    
> >>>
> >>> Correct. Then I should fix spi_controller_check_ops() to allow empty
> >>> ctlr->mem_ops->supports_op.
> >>>     
> >>>>    
> >>>>> This being said, if you think a custom ->supports_op()
> >>>>> implementation is needed for this controller I can add one.
> >>>>>       
> >>>>
> >>>> spi_mem_supports_op() should suffice for now if above issue is fixed.    
> >>>
> >>> Cool. IIUC, you tested the series on a TI SoC. Does it work as
> >>> expected? Do you see any perf regressions?
> >>>     
> 
> No other functional failures or performance issues so far wrt TI QSPI.
> Things look good :)

That's good news. I'll send a v2 addressing the problems you and others
reported soon, but I'd like to have Cyrille's and Mark's feedback first.

BTW, don't hesitate to comment on the interface itself. Do you think
it's missing important features? Do you need more information to better
optimize SPI memory accesses? ...
This truly was an RFC: I'm new to these QSPI/SPI-NOR/SPI-NAND stuff, and
I know you and others already faced various problems and thought about
different solutions to address them. Now is a good time to re-think the
whole thing and design the spi_mem interface in a way that allows us to
better support all these QSPI-memory-oriented controllers.

Regards,

Boris

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RFC PATCH 4/6] spi: ti-qspi: Implement the spi_mem interface
@ 2018-02-17 21:52                               ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-17 21:52 UTC (permalink / raw)
  To: Vignesh R
  Cc: Boris Brezillon, Yogesh Gaur, Kamal Dasu, Richard Weinberger,
	linux-spi, Peter Pan, Marek Vasut, Frieder Schrempf, Mark Brown,
	linux-mtd, Cyrille Pitchen, Rafał Miłecki,
	Brian Norris, David Woodhouse

On Sat, 17 Feb 2018 16:31:48 +0530
Vignesh R <vigneshr@ti.com> wrote:

> [...]
> >>>>>>       
> >>>>>>> +static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
> >>>>>>> +   .exec_op = ti_qspi_exec_mem_op,        
> >>>>>>
> >>>>>>         .supports_op = ti_qspi_supports_mem_op,
> >>>>>>
> >>>>>> Its required as per spi_controller_check_ops() in Patch 1/6      
> >>>>>       
> >>>>> ->supports_op() is optional, and if it's missing, the core will do the      
> >>>>> regular QuadSPI/DualSPI/SingleSPI check (see spi_mem_supports_op()
> >>>>> implementation).       
> >>>>
> >>>> You might have overlooked spi_controller_check_ops() from Patch 1/6:
> >>>> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> >>>> +{
> >>>> +	/*
> >>>> +	 * The controller can implement only the high-level SPI-memory
> >>>> +	 * operations if it does not support regular SPI transfers.
> >>>> +	 */
> >>>> +	if (ctlr->mem_ops) {
> >>>> +		if (!ctlr->mem_ops->supports_op ||
> >>>> +		    !ctlr->mem_ops->exec_op)
> >>>> +			return -EINVAL;
> >>>> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> >>>> +		   !ctlr->transfer_one_message) {
> >>>> +		return -EINVAL;
> >>>> +	}
> >>>> +
> >>>> +	return 0;
> >>>> +}
> >>>> +
> >>>>
> >>>> So if ->supports_op() is not populated by SPI controller driver, then
> >>>> driver probe fails with -EINVAL. This is what I observed on my TI
> >>>> hardware when testing this patch series.    
> >>>
> >>> Correct. Then I should fix spi_controller_check_ops() to allow empty
> >>> ctlr->mem_ops->supports_op.
> >>>     
> >>>>    
> >>>>> This being said, if you think a custom ->supports_op()
> >>>>> implementation is needed for this controller I can add one.
> >>>>>       
> >>>>
> >>>> spi_mem_supports_op() should suffice for now if above issue is fixed.    
> >>>
> >>> Cool. IIUC, you tested the series on a TI SoC. Does it work as
> >>> expected? Do you see any perf regressions?
> >>>     
> 
> No other functional failures or performance issues so far wrt TI QSPI.
> Things look good :)

That's good news. I'll send a v2 addressing the problems you and others
reported soon, but I'd like to have Cyrille's and Mark's feedback first.

BTW, don't hesitate to comment on the interface itself. Do you think
it's missing important features? Do you need more information to better
optimize SPI memory accesses? ...
This truly was an RFC: I'm new to these QSPI/SPI-NOR/SPI-NAND stuff, and
I know you and others already faced various problems and thought about
different solutions to address them. Now is a good time to re-think the
whole thing and design the spi_mem interface in a way that allows us to
better support all these QSPI-memory-oriented controllers.

Regards,

Boris

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-05 23:21     ` Boris Brezillon
  (?)
  (?)
@ 2018-02-19 13:53     ` Mark Brown
  2018-02-19 14:20       ` Boris Brezillon
  -1 siblings, 1 reply; 92+ messages in thread
From: Mark Brown @ 2018-02-19 13:53 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, linux-spi,
	Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

[-- Attachment #1: Type: text/plain, Size: 2462 bytes --]

On Tue, Feb 06, 2018 at 12:21:15AM +0100, Boris Brezillon wrote:

> Some controllers are exposing high-level interfaces to access various
> kind of SPI memories. Unfortunately they do not fit in the current
> spi_controller model and usually have drivers placed in
> drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> memories in general.

> This is an attempt at defining a SPI memory interface which works for
> all kinds of SPI memories (NORs, NANDs, SRAMs).

It would be helpful if this changelog were to describe what the
problems are and how the patch addresses them.  Someone reading the git
history should be able to tell what the patch is doing without having to
google around to find a cover letter for the patch series.  It also
feels like this should be split up a bit - there's some preparatory
refactoring here, a new API, and an adaption layer to implement that API
with actual SPI controllers.  It's a very large patch doing several
different things and splitting it up would make it easier to review.

I have to say I'm rather unclear why this is being done in the SPI core
rather than as a layer on top of it - devices doing the new API can't
support normal SPI clients at all and everything about this is entirely
flash specific.  You've got a bit about that in your cover letter, I'll
reply there.

> @@ -2155,10 +2181,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>  			spi_controller_is_slave(ctlr) ? "slave" : "master",
>  			dev_name(&ctlr->dev));
>  
> -	/* If we're using a queued driver, start the queue */
> -	if (ctlr->transfer)
> +	/*
> +	 * If we're using a queued driver, start the queue. Note that we don't
> +	 * need the queueing logic if the driver is only supporting high-level
> +	 * memory operations.
> +	 */
> +	if (ctlr->transfer) {
>  		dev_info(dev, "controller is unqueued, this is deprecated\n");
> -	else {
> +	} else if (ctlr->transfer_one || ctlr->transfer_one_message) {
>  		status = spi_controller_initialize_queue(ctlr);
>  		if (status) {
>  			device_del(&ctlr->dev);

This for example feels like a separate refactoring which could be split
out.

> +	if (op->data.dir == SPI_MEM_DATA_OUT)
> +		dmadev = ctlr->dma_tx ?
> +			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +	else
> +		dmadev = ctlr->dma_rx ?
> +			 ctlr->dma_rx->device->dev : ctlr->dev.parent;

Please don't abuse the ternery operator like this :(

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-05 23:21     ` Boris Brezillon
                       ` (2 preceding siblings ...)
  (?)
@ 2018-02-19 14:00     ` Mark Brown
  2018-02-19 14:32       ` Boris Brezillon
  -1 siblings, 1 reply; 92+ messages in thread
From: Mark Brown @ 2018-02-19 14:00 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, linux-spi,
	Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

[-- Attachment #1: Type: text/plain, Size: 838 bytes --]

On Tue, Feb 06, 2018 at 12:21:15AM +0100, Boris Brezillon wrote:

> +	/*
> +	 * The controller can implement only the high-level SPI-memory like
> +	 * operations if it does not support regular SPI transfers.
> +	 */
> +	if (ctlr->mem_ops) {
> +		if (!ctlr->mem_ops->supports_op ||
> +		    !ctlr->mem_ops->exec_op)
> +			return -EINVAL;
> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> +		   !ctlr->transfer_one_message) {
> +		return -EINVAL;
> +	}

BTW your comment isn't describing what the code does - the comment says
that having the memory operations means the driver can't be a regular
SPI controller while the code does not do that and only checks that if a
driver has memory ops it implements two required ones.  Indeed the
existing drivers that are updated to the new API continue to implement
normal SPI operations.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-19 13:53     ` Mark Brown
@ 2018-02-19 14:20       ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-19 14:20 UTC (permalink / raw)
  To: Mark Brown
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, linux-spi,
	Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

Hi Mark,

On Mon, 19 Feb 2018 13:53:57 +0000
Mark Brown <broonie@kernel.org> wrote:

> On Tue, Feb 06, 2018 at 12:21:15AM +0100, Boris Brezillon wrote:
> 
> > Some controllers are exposing high-level interfaces to access various
> > kind of SPI memories. Unfortunately they do not fit in the current
> > spi_controller model and usually have drivers placed in
> > drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> > memories in general.  
> 
> > This is an attempt at defining a SPI memory interface which works for
> > all kinds of SPI memories (NORs, NANDs, SRAMs).  
> 
> It would be helpful if this changelog were to describe what the
> problems are and how the patch addresses them.  Someone reading the git
> history should be able to tell what the patch is doing without having to
> google around to find a cover letter for the patch series.

Sure. I'll copy the explanation I give in my cover letter here.

> It also
> feels like this should be split up a bit - there's some preparatory
> refactoring here, a new API, and an adaption layer to implement that API
> with actual SPI controllers.  It's a very large patch doing several
> different things and splitting it up would make it easier to review.

Noted. I'll try to spit things up.

> 
> I have to say I'm rather unclear why this is being done in the SPI core
> rather than as a layer on top of it - devices doing the new API can't
> support normal SPI clients at all and everything about this is entirely
> flash specific.  You've got a bit about that in your cover letter, I'll
> reply there.

Then I'll reply there.

> 
> > @@ -2155,10 +2181,14 @@ int spi_register_controller(struct spi_controller *ctlr)
> >  			spi_controller_is_slave(ctlr) ? "slave" : "master",
> >  			dev_name(&ctlr->dev));
> >  
> > -	/* If we're using a queued driver, start the queue */
> > -	if (ctlr->transfer)
> > +	/*
> > +	 * If we're using a queued driver, start the queue. Note that we don't
> > +	 * need the queueing logic if the driver is only supporting high-level
> > +	 * memory operations.
> > +	 */
> > +	if (ctlr->transfer) {
> >  		dev_info(dev, "controller is unqueued, this is deprecated\n");
> > -	else {
> > +	} else if (ctlr->transfer_one || ctlr->transfer_one_message) {
> >  		status = spi_controller_initialize_queue(ctlr);
> >  		if (status) {
> >  			device_del(&ctlr->dev);  
> 
> This for example feels like a separate refactoring which could be split
> out.

Hm, this change is not really required without the spi_mem stuff. I
mean, the only cases we have right now are:

1/ the controller implements ->transfer() on its own
2/ the controller implements ctlr->transfer_one()
   or ctlr->transfer_one_message() and relies on the default
   queueing/dequeuing mechanism

The spi_mem stuff adds a 3rd case:

3/ the controller only supports memory-like operation and in this case
   we don't need to initialize the queue

which is why I added this else if() at the same time I added the
spi_mem stuff.

> 
> > +	if (op->data.dir == SPI_MEM_DATA_OUT)
> > +		dmadev = ctlr->dma_tx ?
> > +			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
> > +	else
> > +		dmadev = ctlr->dma_rx ?
> > +			 ctlr->dma_rx->device->dev : ctlr->dev.parent;  
> 
> Please don't abuse the ternery operator like this :(

I'll try to remember that.

Thanks for your review.

Boris

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-19 14:00     ` Mark Brown
@ 2018-02-19 14:32       ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-19 14:32 UTC (permalink / raw)
  To: Mark Brown
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, linux-spi,
	Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

On Mon, 19 Feb 2018 14:00:03 +0000
Mark Brown <broonie@kernel.org> wrote:

> On Tue, Feb 06, 2018 at 12:21:15AM +0100, Boris Brezillon wrote:
> 
> > +	/*
> > +	 * The controller can implement only the high-level SPI-memory like
> > +	 * operations if it does not support regular SPI transfers.
> > +	 */
> > +	if (ctlr->mem_ops) {
> > +		if (!ctlr->mem_ops->supports_op ||
> > +		    !ctlr->mem_ops->exec_op)
> > +			return -EINVAL;
> > +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> > +		   !ctlr->transfer_one_message) {
> > +		return -EINVAL;
> > +	}  
> 
> BTW your comment isn't describing what the code does - the comment says
> that having the memory operations means the driver can't be a regular
> SPI controller while the code does not do that and only checks that if a
> driver has memory ops it implements two required ones.  Indeed the
> existing drivers that are updated to the new API continue to implement
> normal SPI operations.

Definitely not what I wanted to say :-/. I guess replacing 'can' by
'may' would be more appropriate. What I want to say is that SPI
controllers do not have to implement the hooks for regular SPI
operations if they only support SPI-mem like operations, but of course
they can implement those hooks if they support both spi_mem and regular
SPI ops.

This check is here to allow registration of SPI controllers that
support spi_mem ops, regular ops or both, and prevent registration if
both spi_mem and regular hooks are missing.

Is it clearer?

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 0/6] spi: Extend the framework to generically support memory devices
  2018-02-05 23:21 ` Boris Brezillon
  (?)
  (?)
@ 2018-02-19 16:25 ` Mark Brown
  2018-02-19 16:51   ` Boris Brezillon
  2018-03-04 21:40     ` Cyrille Pitchen
  -1 siblings, 2 replies; 92+ messages in thread
From: Mark Brown @ 2018-02-19 16:25 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, linux-spi,
	Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

[-- Attachment #1: Type: text/plain, Size: 1555 bytes --]

On Tue, Feb 06, 2018 at 12:21:14AM +0100, Boris Brezillon wrote:

> SPI NAND layer): you can register a SPI NOR device directly from the
> dedicated SPI memory controller, or it can be registered through the
> SPI layer if the SPI controller is a generic SPI controller. While
> the generic SPI controller path works fine, the dedicated SPI NOR
> controller path brings its own set of issues:

> * the SPI bus is not represented in sysfs

I'm not sure if this is a big deal or not - at some point it's just an
implementation detail of the hardware rather than something we're aware
of or interested in.

> * because there's no bus, there's no uevent, which means you'll have to
>   select both the SPI NAND and SPI NOR logic as soon as one driver
>   supports both interfaces if you don't want to run into loading
>   dependency issues

This is sounding like we want a class (well, virtual bus in the new
world) for these devices with a SPI based driver sitting on top of that
for use with genuine SPI controllers.  If the intention is as the
comments in the code suggested that controllers implementing the memory
mapping stuff don't use SPI at all then we could have the legacy SPI bus
support be just another driver for this class.  However when I look at
what the drivers are actually doing it seems like that's not the case
and the new API is intended to sit alongside normal SPI support, perhaps
only implementing certain operations and using regular SPI for others.
In that case it makes a lot more sense to have this be bolted on the
side of SPI.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [RFC PATCH 0/6] spi: Extend the framework to generically support memory devices
  2018-02-19 16:25 ` [RFC PATCH 0/6] spi: Extend the framework to generically support memory devices Mark Brown
@ 2018-02-19 16:51   ` Boris Brezillon
  2018-03-04 21:40     ` Cyrille Pitchen
  1 sibling, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-19 16:51 UTC (permalink / raw)
  To: Mark Brown
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, linux-spi,
	Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

On Mon, 19 Feb 2018 16:25:10 +0000
Mark Brown <broonie@kernel.org> wrote:

> On Tue, Feb 06, 2018 at 12:21:14AM +0100, Boris Brezillon wrote:
> 
> > SPI NAND layer): you can register a SPI NOR device directly from the
> > dedicated SPI memory controller, or it can be registered through the
> > SPI layer if the SPI controller is a generic SPI controller. While
> > the generic SPI controller path works fine, the dedicated SPI NOR
> > controller path brings its own set of issues:  
> 
> > * the SPI bus is not represented in sysfs  
> 
> I'm not sure if this is a big deal or not - at some point it's just an
> implementation detail of the hardware rather than something we're aware
> of or interested in.
> 
> > * because there's no bus, there's no uevent, which means you'll have to
> >   select both the SPI NAND and SPI NOR logic as soon as one driver
> >   supports both interfaces if you don't want to run into loading
> >   dependency issues  
> 
> This is sounding like we want a class (well, virtual bus in the new
> world) for these devices with a SPI based driver sitting on top of that
> for use with genuine SPI controllers.  If the intention is as the
> comments in the code suggested that controllers implementing the memory
> mapping stuff don't use SPI at all then we could have the legacy SPI bus
> support be just another driver for this class.  However when I look at
> what the drivers are actually doing it seems like that's not the case
> and the new API is intended to sit alongside normal SPI support, perhaps
> only implementing certain operations and using regular SPI for others.
> In that case it makes a lot more sense to have this be bolted on the
> side of SPI.

This is a case: most controllers support both regular SPI transfers and
memory-like operations (with some optimizations for memory oriented
operations).

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
http://bootlin.com

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-05 23:21     ` Boris Brezillon
@ 2018-02-28  7:51       ` Peter Pan
  -1 siblings, 0 replies; 92+ messages in thread
From: Peter Pan @ 2018-02-28  7:51 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Boris Brezillon, Yogesh Gaur, Vignesh R, Kamal Dasu,
	Richard Weinberger, linux-spi, Marek Vasut, Frieder Schrempf,
	Mark Brown, linux-mtd, Cyrille Pitchen, Rafał Miłecki,
	Sourav Poddar, Brian Norris, David Woodhouse

Hi Boris,

I'm away from work for the last month for some personal reason.
Now I'm trying to catch up your great effort on SPI memory stuff.

Below is some comments:


On Tue, Feb 6, 2018 at 7:21 AM, Boris Brezillon
<boris.brezillon@bootlin.com> wrote:
> From: Boris Brezillon <boris.brezillon@free-electrons.com>
>
> Some controllers are exposing high-level interfaces to access various
> kind of SPI memories. Unfortunately they do not fit in the current
> spi_controller model and usually have drivers placed in
> drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> memories in general.
>
> This is an attempt at defining a SPI memory interface which works for
> all kinds of SPI memories (NORs, NANDs, SRAMs).
>
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> ---
>  drivers/spi/spi.c       | 423 +++++++++++++++++++++++++++++++++++++++++++++++-
>  include/linux/spi/spi.h | 226 ++++++++++++++++++++++++++
>  2 files changed, 646 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index b33a727a0158..57bc540a0521 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -2057,6 +2057,24 @@ static int of_spi_register_master(struct spi_controller *ctlr)
>  }
>  #endif
>
> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> +{
> +       /*
> +        * The controller can implement only the high-level SPI-memory like
> +        * operations if it does not support regular SPI transfers.
> +        */
> +       if (ctlr->mem_ops) {
> +               if (!ctlr->mem_ops->supports_op ||
> +                   !ctlr->mem_ops->exec_op)
> +                       return -EINVAL;
> +       } else if (!ctlr->transfer && !ctlr->transfer_one &&
> +                  !ctlr->transfer_one_message) {
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
>  /**
>   * spi_register_controller - register SPI master or slave controller
>   * @ctlr: initialized master, originally from spi_alloc_master() or
> @@ -2090,6 +2108,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>         if (!dev)
>                 return -ENODEV;
>
> +       /*
> +        * Make sure all necessary hooks are implemented before registering
> +        * the SPI controller.
> +        */
> +       status = spi_controller_check_ops(ctlr);
> +       if (status)
> +               return status;
> +
>         if (!spi_controller_is_slave(ctlr)) {
>                 status = of_spi_register_master(ctlr);
>                 if (status)
> @@ -2155,10 +2181,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>                         spi_controller_is_slave(ctlr) ? "slave" : "master",
>                         dev_name(&ctlr->dev));
>
> -       /* If we're using a queued driver, start the queue */
> -       if (ctlr->transfer)
> +       /*
> +        * If we're using a queued driver, start the queue. Note that we don't
> +        * need the queueing logic if the driver is only supporting high-level
> +        * memory operations.
> +        */
> +       if (ctlr->transfer) {
>                 dev_info(dev, "controller is unqueued, this is deprecated\n");
> -       else {
> +       } else if (ctlr->transfer_one || ctlr->transfer_one_message) {
>                 status = spi_controller_initialize_queue(ctlr);
>                 if (status) {
>                         device_del(&ctlr->dev);
> @@ -2893,6 +2923,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
>  {
>         struct spi_controller *ctlr = spi->controller;
>
> +       /*
> +        * Some controllers do not support doing regular SPI transfers. Return
> +        * ENOTSUPP when this is the case.
> +        */
> +       if (!ctlr->transfer)
> +               return -ENOTSUPP;
> +
>         message->spi = spi;
>
>         SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_async);
> @@ -3321,6 +3358,386 @@ int spi_write_then_read(struct spi_device *spi,
>  }
>  EXPORT_SYMBOL_GPL(spi_write_then_read);
>
> +/**
> + * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a
> + *                                       memory operation
> + * @ctlr: the SPI controller requesting this dma_map()
> + * @op: the memory operation containing the buffer to map
> + * @sgt: a pointer to a non-initialized sg_table that will be filled by this
> + *      function
> + *
> + * Some controllers might want to do DMA on the data buffer embedded in @op.
> + * This helper prepares everything for you and provides a ready-to-use
> + * sg_table. This function is not intended to be called from spi drivers.
> + * Only SPI controller drivers should use it.
> + * Note that the caller must ensure the memory region pointed by
> + * op->data.buf.{in,out} is DMA-able before calling this function.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
> +                                      const struct spi_mem_op *op,
> +                                      struct sg_table *sgt)
> +{
> +       struct device *dmadev;
> +
> +       if (!op->data.nbytes)
> +               return -EINVAL;
> +
> +       if (op->data.dir == SPI_MEM_DATA_OUT)
> +               dmadev = ctlr->dma_tx ?
> +                        ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +       else
> +               dmadev = ctlr->dma_rx ?
> +                        ctlr->dma_rx->device->dev : ctlr->dev.parent;
> +
> +       if (!dmadev)
> +               return -EINVAL;
> +
> +       return spi_map_buf(ctlr, dmadev, sgt, op->data.buf.in, op->data.nbytes,
> +                          op->data.dir == SPI_MEM_DATA_IN ?
> +                          DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL_GPL(spi_controller_dma_map_mem_op_data);
> +
> +/**
> + * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a
> + *                                         memory operation
> + * @ctlr: the SPI controller requesting this dma_unmap()
> + * @op: the memory operation containing the buffer to unmap
> + * @sgt: a pointer to an sg_table previously initialized by
> + *      spi_controller_dma_map_mem_op_data()
> + *
> + * Some controllers might want to do DMA on the data buffer embedded in @op.
> + * This helper prepares things so that the CPU can access the
> + * op->data.buf.{in,out} buffer again.
> + *
> + * This function is not intended to be called from spi drivers. Only SPI
> + * controller drivers should use it.
> + *
> + * This function should be called after the DMA operation has finished an is
> + * only valid if the previous spi_controller_dma_map_mem_op_data() has returned
> + * 0.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> +                                         const struct spi_mem_op *op,
> +                                         struct sg_table *sgt)
> +{
> +       struct device *dmadev;
> +
> +       if (!op->data.nbytes)
> +               return;
> +
> +       if (op->data.dir == SPI_MEM_DATA_OUT)
> +               dmadev = ctlr->dma_tx ?
> +                        ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +       else
> +               dmadev = ctlr->dma_rx ?
> +                        ctlr->dma_rx->device->dev : ctlr->dev.parent;
> +
> +       spi_unmap_buf(ctlr, dmadev, sgt,
> +                     op->data.dir == SPI_MEM_DATA_IN ?
> +                     DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data);
> +
> +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
> +{
> +       u32 mode = mem->spi->mode;
> +
> +       switch (buswidth) {
> +       case 1:
> +               return 0;
> +
> +       case 2:
> +               if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> +                   (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
> +                       return 0;
> +
> +               break;
> +
> +       case 4:
> +               if ((tx && (mode & SPI_TX_QUAD)) ||
> +                   (!tx && (mode & SPI_RX_QUAD)))
> +                       return 0;
> +
> +               break;
> +
> +       default:
> +               break;
> +       }
> +
> +       return -ENOTSUPP;
> +}
> +
> +/**
> + * spi_mem_supports_op() - Check if a memory device and the controller it is
> + *                        connected to support a specific memory operation
> + * @mem: the SPI memory
> + * @op: the memory operation to check
> + *
> + * Some controllers are only supporting Single or Dual IOs, others might only
> + * support specific opcodes, or it can even be that the controller and device
> + * both support Quad IOs but the hardware prevents you from using it because
> + * only 2 IO lines are connected.
> + *
> + * This function checks whether a specific operation is supported.
> + *
> + * Return: true if @op is supported, false otherwise.
> + */
> +bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +       struct spi_controller *ctlr = mem->spi->controller;
> +
> +       if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
> +               return false;
> +
> +       if (op->addr.nbytes &&
> +           spi_check_buswidth_req(mem, op->addr.buswidth, true))
> +               return false;
> +
> +       if (op->dummy.nbytes &&
> +           spi_check_buswidth_req(mem, op->dummy.buswidth, true))
> +               return false;
> +
> +       if (op->data.nbytes &&
> +           spi_check_buswidth_req(mem, op->data.buswidth,
> +                                  op->data.dir == SPI_MEM_DATA_IN ?
> +                                  false : true))
> +               return false;
> +
> +       if (ctlr->mem_ops)
> +               return ctlr->mem_ops->supports_op(mem, op);
> +
> +       return true;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_supports_op);
> +
> +/**
> + * spi_mem_exec_op() - Execute a memory operation
> + * @mem: the SPI memory
> + * @op: the memory operation to execute
> + *
> + * Executes a memory operation.
> + *
> + * This function first checks that @op is supported and then tries to execute
> + * it.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +       unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> +       struct spi_controller *ctlr = mem->spi->controller;
> +       struct spi_transfer xfers[4] = { };
> +       struct spi_message msg;
> +       u8 *tmpbuf;
> +       int ret;
> +
> +       if (!spi_mem_supports_op(mem, op))
> +               return -ENOTSUPP;
> +
> +       if (ctlr->mem_ops) {
> +               if (ctlr->auto_runtime_pm) {
> +                       ret = pm_runtime_get_sync(ctlr->dev.parent);
> +                       if (ret < 0) {
> +                               dev_err(&ctlr->dev,
> +                                       "Failed to power device: %d\n",
> +                                       ret);
> +                               return ret;
> +                       }
> +               }
> +
> +               mutex_lock(&ctlr->bus_lock_mutex);
> +               mutex_lock(&ctlr->io_mutex);
> +               ret = ctlr->mem_ops->exec_op(mem, op);
> +               mutex_unlock(&ctlr->io_mutex);
> +               mutex_unlock(&ctlr->bus_lock_mutex);
> +
> +               if (ctlr->auto_runtime_pm)
> +                       pm_runtime_put(ctlr->dev.parent);
> +
> +               /*
> +                * Some controllers only optimize specific paths (typically the
> +                * read path) and expect the core to use the regular SPI
> +                * interface in these cases.
> +                */
> +               if (!ret || ret != -ENOTSUPP)
> +                       return ret;
> +       }
> +
> +       tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
> +                    op->dummy.nbytes;

Here you are using sizeof(op->cmd.opcode) while the code after this
assumes opcode is u8(ie. "memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);")
It may be confused.

Thanks,
Peter Pan

> +
> +       /*
> +        * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
> +        * we're guaranteed that this buffer is DMA-able, as required by the
> +        * SPI layer.
> +        */
> +       tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
> +       if (!tmpbuf)
> +               return -ENOMEM;
> +
> +       spi_message_init(&msg);
> +
> +       tmpbuf[0] = op->cmd.opcode;
> +       xfers[xferpos].tx_buf = tmpbuf;
> +       xfers[xferpos].len = sizeof(op->cmd.opcode);
> +       xfers[xferpos].tx_nbits = op->cmd.buswidth;
> +       spi_message_add_tail(&xfers[xferpos], &msg);
> +       xferpos++;
> +       totalxferlen++;
> +
> +       if (op->addr.nbytes) {
> +               memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);
> +               xfers[xferpos].tx_buf = tmpbuf + 1;
> +               xfers[xferpos].len = op->addr.nbytes;
> +               xfers[xferpos].tx_nbits = op->addr.buswidth;
> +               spi_message_add_tail(&xfers[xferpos], &msg);
> +               xferpos++;
> +               totalxferlen += op->addr.nbytes;
> +       }
> +
> +       if (op->dummy.nbytes) {
> +               memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes);
> +               xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
> +               xfers[xferpos].len = op->dummy.nbytes;
> +               xfers[xferpos].tx_nbits = op->dummy.buswidth;
> +               spi_message_add_tail(&xfers[xferpos], &msg);
> +               xferpos++;
> +               totalxferlen += op->dummy.nbytes;
> +       }
> +
> +       if (op->data.nbytes) {
> +               if (op->data.dir == SPI_MEM_DATA_IN) {
> +                       xfers[xferpos].rx_buf = op->data.buf.in;
> +                       xfers[xferpos].rx_nbits = op->data.buswidth;
> +               } else {
> +                       xfers[xferpos].tx_buf = op->data.buf.out;
> +                       xfers[xferpos].tx_nbits = op->data.buswidth;
> +               }
> +
> +               xfers[xferpos].len = op->data.nbytes;
> +               spi_message_add_tail(&xfers[xferpos], &msg);
> +               xferpos++;
> +               totalxferlen += op->data.nbytes;
> +       }
> +
> +       ret = spi_sync(mem->spi, &msg);
> +
> +       kfree(tmpbuf);
> +
> +       if (ret)
> +               return ret;
> +
> +       if (msg.actual_length != totalxferlen)
> +               return -EIO;
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_exec_op);
> +
> +/**
> + * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
> + *                           match controller limitations
> + * @mem: the SPI memory
> + * @op: the operation to adjust
> + *
> + * Some controllers have FIFO limitations and must split a data transfer
> + * operation into multiple ones, others require a specific alignment for
> + * optimized accesses. This function allows SPI mem drivers to split a single
> + * operation into multiple sub-operations when required.
> + *
> + * Return: a negative error code if the controller can't properly adjust @op,
> + *        0 otherwise. Note that @op->data.nbytes will be updated if @op
> + *        can't be handled in a single step.
> + */
> +int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
> +{
> +       struct spi_controller *ctlr = mem->spi->controller;
> +
> +       if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
> +               return ctlr->mem_ops->adjust_op_size(mem, op);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
> +
> +static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
> +{
> +       return container_of(drv, struct spi_mem_driver, spidrv.driver);
> +}
> +
> +static int spi_mem_probe(struct spi_device *spi)
> +{
> +       struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +       struct spi_mem *mem;
> +
> +       mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL);
> +       if (!mem)
> +               return -ENOMEM;
> +
> +       mem->spi = spi;
> +       spi_set_drvdata(spi, mem);
> +
> +       return memdrv->probe(mem);
> +}
> +
> +static int spi_mem_remove(struct spi_device *spi)
> +{
> +       struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +       struct spi_mem *mem = spi_get_drvdata(spi);
> +
> +       if (memdrv->remove)
> +               return memdrv->remove(mem);
> +
> +       return 0;
> +}
> +
> +static void spi_mem_shutdown(struct spi_device *spi)
> +{
> +       struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +       struct spi_mem *mem = spi_get_drvdata(spi);
> +
> +       if (memdrv->shutdown)
> +               memdrv->shutdown(mem);
> +}
> +
> +/**
> + * spi_mem_driver_register_with_owner() - Register a SPI memory driver
> + * @memdrv: the SPI memory driver to register
> + * @owner: the owner of this driver
> + *
> + * Registers a SPI memory driver.
> + *
> + * Return: 0 in case of success, a negative error core otherwise.
> + */
> +
> +int spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv,
> +                                      struct module *owner)
> +{
> +       memdrv->spidrv.probe = spi_mem_probe;
> +       memdrv->spidrv.remove = spi_mem_remove;
> +       memdrv->spidrv.shutdown = spi_mem_shutdown;
> +
> +       return __spi_register_driver(owner, &memdrv->spidrv);
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner);
> +
> +/**
> + * spi_mem_driver_unregister_with_owner() - Unregister a SPI memory driver
> + * @memdrv: the SPI memory driver to unregister
> + *
> + * Unregisters a SPI memory driver.
> + */
> +void spi_mem_driver_unregister(struct spi_mem_driver *memdrv)
> +{
> +       spi_unregister_driver(&memdrv->spidrv);
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_driver_unregister);
> +
>  /*-------------------------------------------------------------------------*/
>
>  #if IS_ENABLED(CONFIG_OF_DYNAMIC)
> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> index 7b2170bfd6e7..af3c4ac62b55 100644
> --- a/include/linux/spi/spi.h
> +++ b/include/linux/spi/spi.h
> @@ -27,6 +27,7 @@ struct property_entry;
>  struct spi_controller;
>  struct spi_transfer;
>  struct spi_flash_read_message;
> +struct spi_controller_mem_ops;
>
>  /*
>   * INTERFACES between SPI master-side drivers and SPI slave protocol handlers,
> @@ -376,6 +377,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
>   *                    transfer_one callback.
>   * @handle_err: the subsystem calls the driver to handle an error that occurs
>   *             in the generic implementation of transfer_one_message().
> + * @mem_ops: optimized/dedicated operations for interactions with SPI memory.
> + *          This field is optional and should only be implemented if the
> + *          controller has native support for memory like operations.
>   * @unprepare_message: undo any work done by prepare_message().
>   * @slave_abort: abort the ongoing transfer request on an SPI slave controller
>   * @spi_flash_read: to support spi-controller hardwares that provide
> @@ -564,6 +568,9 @@ struct spi_controller {
>         void (*handle_err)(struct spi_controller *ctlr,
>                            struct spi_message *message);
>
> +       /* Optimized handlers for SPI memory-like operations. */
> +       const struct spi_controller_mem_ops *mem_ops;
> +
>         /* gpio chip select */
>         int                     *cs_gpios;
>
> @@ -1227,6 +1234,225 @@ int spi_flash_read(struct spi_device *spi,
>
>  /*---------------------------------------------------------------------------*/
>
> +/* SPI memory related definitions. */
> +
> +#define SPI_MEM_OP_CMD(__opcode, __buswidth)                   \
> +       {                                                       \
> +               .buswidth = __buswidth,                         \
> +               .opcode = __opcode,                             \
> +       }
> +
> +#define SPI_MEM_OP_ADDRS(__nbytes, __buf, __buswidth)          \
> +       {                                                       \
> +               .nbytes = __nbytes,                             \
> +               .buf = __buf,                                   \
> +               .buswidth = __buswidth,                         \
> +       }
> +
> +#define SPI_MEM_OP_NO_ADDRS    { }
> +
> +#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth)                 \
> +       {                                                       \
> +               .nbytes = __nbytes,                             \
> +               .buswidth = __buswidth,                         \
> +       }
> +
> +#define SPI_MEM_OP_NO_DUMMY    { }
> +
> +#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth)                \
> +       {                                                       \
> +               .dir = SPI_MEM_DATA_IN,                         \
> +               .nbytes = __nbytes,                             \
> +               .buf.in = __buf,                                \
> +               .buswidth = __buswidth,                         \
> +       }
> +
> +#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth)       \
> +       {                                                       \
> +               .dir = SPI_MEM_DATA_OUT,                        \
> +               .nbytes = __nbytes,                             \
> +               .buf.out = __buf,                               \
> +               .buswidth = __buswidth,                         \
> +       }
> +
> +#define SPI_MEM_OP_NO_DATA     { }
> +
> +/**
> + * enum spi_mem_data_dir - describes the direction of a SPI memory data
> + *                        transfer from the controller perspective
> + * @SPI_MEM_DATA_IN: data coming from the SPI memory
> + * @SPI_MEM_DATA_OUT: data sent the SPI memory
> + */
> +enum spi_mem_data_dir {
> +       SPI_MEM_DATA_IN,
> +       SPI_MEM_DATA_OUT,
> +};
> +
> +/**
> + * struct spi_mem_op - describes a SPI memory operation
> + * @cmd.buswidth: number of IO lines used to transmit the command
> + * @cmd.opcode: operation opcode
> + * @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.buf: buffer storing the address bytes
> + * @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
> + * @data.buswidth: number of IO lanes used to send/receive the data
> + * @data.dir: direction of the transfer
> + * @data.buf.in: input buffer
> + * @data.buf.out: output buffer
> + */
> +struct spi_mem_op {
> +       struct {
> +               u8 buswidth;
> +               u8 opcode;
> +       } cmd;
> +
> +       struct {
> +               u8 nbytes;
> +               u8 buswidth;
> +               const u8 *buf;
> +       } addr;
> +
> +       struct {
> +               u8 nbytes;
> +               u8 buswidth;
> +       } dummy;
> +
> +       struct {
> +               u8 buswidth;
> +               enum spi_mem_data_dir dir;
> +               unsigned int nbytes;
> +               /* buf.{in,out} must be DMA-able. */
> +               union {
> +                       void *in;
> +                       const void *out;
> +               } buf;
> +       } data;
> +};
> +
> +#define SPI_MEM_OP(__cmd, __addr, __dummy, __data)             \
> +       {                                                       \
> +               .cmd = __cmd,                                   \
> +               .addr = __addr,                                 \
> +               .dummy = __dummy,                               \
> +               .data = __data,                                 \
> +       }
> +
> +/**
> + * struct spi_mem - describes a SPI memory device
> + * @spi: the underlying SPI device
> + * @drvpriv: spi_mem_drviver private data
> + *
> + * Extra information that describe the SPI memory device and may be needed by
> + * the controller to properly handle this device should be placed here.
> + *
> + * One example would be the device size since some controller expose their SPI
> + * mem devices through a io-mapped region.
> + */
> +struct spi_mem {
> +       struct spi_device *spi;
> +       void *drvpriv;
> +};
> +
> +/**
> + * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
> + *                               device
> + * @mem: memory device
> + * @data: data to attach to the memory device
> + */
> +static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
> +{
> +       mem->drvpriv = data;
> +}
> +
> +/**
> + * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
> + *                               device
> + * @mem: memory device
> + *
> + * Return: the data attached to the mem device.
> + */
> +static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
> +{
> +       return mem->drvpriv;
> +}
> +
> +/**
> + * 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)
> + * @supports_op: check if an operation is supported by the controller
> + * @exec_op: execute a SPI memory operation
> + *
> + * 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.
> + */
> +struct spi_controller_mem_ops {
> +       int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
> +       bool (*supports_op)(struct spi_mem *mem,
> +                           const struct spi_mem_op *op);
> +       int (*exec_op)(struct spi_mem *mem,
> +                      const struct spi_mem_op *op);
> +};
> +
> +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
> +                                      const struct spi_mem_op *op,
> +                                      struct sg_table *sg);
> +
> +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> +                                         const struct spi_mem_op *op,
> +                                         struct sg_table *sg);
> +
> +int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
> +
> +bool spi_mem_supports_op(struct spi_mem *mem,
> +                        const struct spi_mem_op *op);
> +
> +int spi_mem_exec_op(struct spi_mem *mem,
> +                   const struct spi_mem_op *op);
> +
> +/**
> + * struct spi_mem_driver - SPI memory driver
> + * @spidrv: inherit from a SPI driver
> + * @probe: probe a SPI memory. Usually where detection/initialization takes
> + *        place
> + * @remove: remove a SPI memory
> + * @shutdown: take appropriate action when the system is shutdown
> + *
> + * This is just a thin wrapper around a spi_driver. The core takes care of
> + * allocating the spi_mem object and forwarding the probe/remove/shutdown
> + * request to the spi_mem_driver. The reason we use this wrapper is because
> + * we might have to stuff more information into the spi_mem struct to let
> + * SPI controllers know more about the SPI memory they interact with, and
> + * having this intermediate layer allows us to do that without adding more
> + * useless fields to the spi_device object.
> + */
> +struct spi_mem_driver {
> +       struct spi_driver spidrv;
> +       int (*probe)(struct spi_mem *mem);
> +       int (*remove)(struct spi_mem *mem);
> +       void (*shutdown)(struct spi_mem *mem);
> +};
> +
> +int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
> +                                      struct module *owner);
> +
> +#define spi_mem_driver_register(__drv)                                  \
> +       spi_mem_driver_register_with_owner(__drv, THIS_MODULE)
> +
> +void spi_mem_driver_unregister(struct spi_mem_driver *drv);
> +
> +#define module_spi_mem_driver(__drv)                                    \
> +       module_driver(__drv, spi_mem_driver_register,                   \
> +                     spi_mem_driver_unregister)
> +
> +/*---------------------------------------------------------------------------*/
> +
>  /*
>   * INTERFACE between board init code and SPI infrastructure.
>   *
> --
> 2.14.1
>

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-02-28  7:51       ` Peter Pan
  0 siblings, 0 replies; 92+ messages in thread
From: Peter Pan @ 2018-02-28  7:51 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

Hi Boris,

I'm away from work for the last month for some personal reason.
Now I'm trying to catch up your great effort on SPI memory stuff.

Below is some comments:


On Tue, Feb 6, 2018 at 7:21 AM, Boris Brezillon
<boris.brezillon@bootlin.com> wrote:
> From: Boris Brezillon <boris.brezillon@free-electrons.com>
>
> Some controllers are exposing high-level interfaces to access various
> kind of SPI memories. Unfortunately they do not fit in the current
> spi_controller model and usually have drivers placed in
> drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> memories in general.
>
> This is an attempt at defining a SPI memory interface which works for
> all kinds of SPI memories (NORs, NANDs, SRAMs).
>
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> ---
>  drivers/spi/spi.c       | 423 +++++++++++++++++++++++++++++++++++++++++++++++-
>  include/linux/spi/spi.h | 226 ++++++++++++++++++++++++++
>  2 files changed, 646 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index b33a727a0158..57bc540a0521 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -2057,6 +2057,24 @@ static int of_spi_register_master(struct spi_controller *ctlr)
>  }
>  #endif
>
> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> +{
> +       /*
> +        * The controller can implement only the high-level SPI-memory like
> +        * operations if it does not support regular SPI transfers.
> +        */
> +       if (ctlr->mem_ops) {
> +               if (!ctlr->mem_ops->supports_op ||
> +                   !ctlr->mem_ops->exec_op)
> +                       return -EINVAL;
> +       } else if (!ctlr->transfer && !ctlr->transfer_one &&
> +                  !ctlr->transfer_one_message) {
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
>  /**
>   * spi_register_controller - register SPI master or slave controller
>   * @ctlr: initialized master, originally from spi_alloc_master() or
> @@ -2090,6 +2108,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>         if (!dev)
>                 return -ENODEV;
>
> +       /*
> +        * Make sure all necessary hooks are implemented before registering
> +        * the SPI controller.
> +        */
> +       status = spi_controller_check_ops(ctlr);
> +       if (status)
> +               return status;
> +
>         if (!spi_controller_is_slave(ctlr)) {
>                 status = of_spi_register_master(ctlr);
>                 if (status)
> @@ -2155,10 +2181,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>                         spi_controller_is_slave(ctlr) ? "slave" : "master",
>                         dev_name(&ctlr->dev));
>
> -       /* If we're using a queued driver, start the queue */
> -       if (ctlr->transfer)
> +       /*
> +        * If we're using a queued driver, start the queue. Note that we don't
> +        * need the queueing logic if the driver is only supporting high-level
> +        * memory operations.
> +        */
> +       if (ctlr->transfer) {
>                 dev_info(dev, "controller is unqueued, this is deprecated\n");
> -       else {
> +       } else if (ctlr->transfer_one || ctlr->transfer_one_message) {
>                 status = spi_controller_initialize_queue(ctlr);
>                 if (status) {
>                         device_del(&ctlr->dev);
> @@ -2893,6 +2923,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
>  {
>         struct spi_controller *ctlr = spi->controller;
>
> +       /*
> +        * Some controllers do not support doing regular SPI transfers. Return
> +        * ENOTSUPP when this is the case.
> +        */
> +       if (!ctlr->transfer)
> +               return -ENOTSUPP;
> +
>         message->spi = spi;
>
>         SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_async);
> @@ -3321,6 +3358,386 @@ int spi_write_then_read(struct spi_device *spi,
>  }
>  EXPORT_SYMBOL_GPL(spi_write_then_read);
>
> +/**
> + * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a
> + *                                       memory operation
> + * @ctlr: the SPI controller requesting this dma_map()
> + * @op: the memory operation containing the buffer to map
> + * @sgt: a pointer to a non-initialized sg_table that will be filled by this
> + *      function
> + *
> + * Some controllers might want to do DMA on the data buffer embedded in @op.
> + * This helper prepares everything for you and provides a ready-to-use
> + * sg_table. This function is not intended to be called from spi drivers.
> + * Only SPI controller drivers should use it.
> + * Note that the caller must ensure the memory region pointed by
> + * op->data.buf.{in,out} is DMA-able before calling this function.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
> +                                      const struct spi_mem_op *op,
> +                                      struct sg_table *sgt)
> +{
> +       struct device *dmadev;
> +
> +       if (!op->data.nbytes)
> +               return -EINVAL;
> +
> +       if (op->data.dir == SPI_MEM_DATA_OUT)
> +               dmadev = ctlr->dma_tx ?
> +                        ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +       else
> +               dmadev = ctlr->dma_rx ?
> +                        ctlr->dma_rx->device->dev : ctlr->dev.parent;
> +
> +       if (!dmadev)
> +               return -EINVAL;
> +
> +       return spi_map_buf(ctlr, dmadev, sgt, op->data.buf.in, op->data.nbytes,
> +                          op->data.dir == SPI_MEM_DATA_IN ?
> +                          DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL_GPL(spi_controller_dma_map_mem_op_data);
> +
> +/**
> + * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a
> + *                                         memory operation
> + * @ctlr: the SPI controller requesting this dma_unmap()
> + * @op: the memory operation containing the buffer to unmap
> + * @sgt: a pointer to an sg_table previously initialized by
> + *      spi_controller_dma_map_mem_op_data()
> + *
> + * Some controllers might want to do DMA on the data buffer embedded in @op.
> + * This helper prepares things so that the CPU can access the
> + * op->data.buf.{in,out} buffer again.
> + *
> + * This function is not intended to be called from spi drivers. Only SPI
> + * controller drivers should use it.
> + *
> + * This function should be called after the DMA operation has finished an is
> + * only valid if the previous spi_controller_dma_map_mem_op_data() has returned
> + * 0.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> +                                         const struct spi_mem_op *op,
> +                                         struct sg_table *sgt)
> +{
> +       struct device *dmadev;
> +
> +       if (!op->data.nbytes)
> +               return;
> +
> +       if (op->data.dir == SPI_MEM_DATA_OUT)
> +               dmadev = ctlr->dma_tx ?
> +                        ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +       else
> +               dmadev = ctlr->dma_rx ?
> +                        ctlr->dma_rx->device->dev : ctlr->dev.parent;
> +
> +       spi_unmap_buf(ctlr, dmadev, sgt,
> +                     op->data.dir == SPI_MEM_DATA_IN ?
> +                     DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data);
> +
> +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
> +{
> +       u32 mode = mem->spi->mode;
> +
> +       switch (buswidth) {
> +       case 1:
> +               return 0;
> +
> +       case 2:
> +               if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> +                   (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
> +                       return 0;
> +
> +               break;
> +
> +       case 4:
> +               if ((tx && (mode & SPI_TX_QUAD)) ||
> +                   (!tx && (mode & SPI_RX_QUAD)))
> +                       return 0;
> +
> +               break;
> +
> +       default:
> +               break;
> +       }
> +
> +       return -ENOTSUPP;
> +}
> +
> +/**
> + * spi_mem_supports_op() - Check if a memory device and the controller it is
> + *                        connected to support a specific memory operation
> + * @mem: the SPI memory
> + * @op: the memory operation to check
> + *
> + * Some controllers are only supporting Single or Dual IOs, others might only
> + * support specific opcodes, or it can even be that the controller and device
> + * both support Quad IOs but the hardware prevents you from using it because
> + * only 2 IO lines are connected.
> + *
> + * This function checks whether a specific operation is supported.
> + *
> + * Return: true if @op is supported, false otherwise.
> + */
> +bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +       struct spi_controller *ctlr = mem->spi->controller;
> +
> +       if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
> +               return false;
> +
> +       if (op->addr.nbytes &&
> +           spi_check_buswidth_req(mem, op->addr.buswidth, true))
> +               return false;
> +
> +       if (op->dummy.nbytes &&
> +           spi_check_buswidth_req(mem, op->dummy.buswidth, true))
> +               return false;
> +
> +       if (op->data.nbytes &&
> +           spi_check_buswidth_req(mem, op->data.buswidth,
> +                                  op->data.dir == SPI_MEM_DATA_IN ?
> +                                  false : true))
> +               return false;
> +
> +       if (ctlr->mem_ops)
> +               return ctlr->mem_ops->supports_op(mem, op);
> +
> +       return true;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_supports_op);
> +
> +/**
> + * spi_mem_exec_op() - Execute a memory operation
> + * @mem: the SPI memory
> + * @op: the memory operation to execute
> + *
> + * Executes a memory operation.
> + *
> + * This function first checks that @op is supported and then tries to execute
> + * it.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +       unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> +       struct spi_controller *ctlr = mem->spi->controller;
> +       struct spi_transfer xfers[4] = { };
> +       struct spi_message msg;
> +       u8 *tmpbuf;
> +       int ret;
> +
> +       if (!spi_mem_supports_op(mem, op))
> +               return -ENOTSUPP;
> +
> +       if (ctlr->mem_ops) {
> +               if (ctlr->auto_runtime_pm) {
> +                       ret = pm_runtime_get_sync(ctlr->dev.parent);
> +                       if (ret < 0) {
> +                               dev_err(&ctlr->dev,
> +                                       "Failed to power device: %d\n",
> +                                       ret);
> +                               return ret;
> +                       }
> +               }
> +
> +               mutex_lock(&ctlr->bus_lock_mutex);
> +               mutex_lock(&ctlr->io_mutex);
> +               ret = ctlr->mem_ops->exec_op(mem, op);
> +               mutex_unlock(&ctlr->io_mutex);
> +               mutex_unlock(&ctlr->bus_lock_mutex);
> +
> +               if (ctlr->auto_runtime_pm)
> +                       pm_runtime_put(ctlr->dev.parent);
> +
> +               /*
> +                * Some controllers only optimize specific paths (typically the
> +                * read path) and expect the core to use the regular SPI
> +                * interface in these cases.
> +                */
> +               if (!ret || ret != -ENOTSUPP)
> +                       return ret;
> +       }
> +
> +       tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
> +                    op->dummy.nbytes;

Here you are using sizeof(op->cmd.opcode) while the code after this
assumes opcode is u8(ie. "memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);")
It may be confused.

Thanks,
Peter Pan

> +
> +       /*
> +        * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
> +        * we're guaranteed that this buffer is DMA-able, as required by the
> +        * SPI layer.
> +        */
> +       tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
> +       if (!tmpbuf)
> +               return -ENOMEM;
> +
> +       spi_message_init(&msg);
> +
> +       tmpbuf[0] = op->cmd.opcode;
> +       xfers[xferpos].tx_buf = tmpbuf;
> +       xfers[xferpos].len = sizeof(op->cmd.opcode);
> +       xfers[xferpos].tx_nbits = op->cmd.buswidth;
> +       spi_message_add_tail(&xfers[xferpos], &msg);
> +       xferpos++;
> +       totalxferlen++;
> +
> +       if (op->addr.nbytes) {
> +               memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);
> +               xfers[xferpos].tx_buf = tmpbuf + 1;
> +               xfers[xferpos].len = op->addr.nbytes;
> +               xfers[xferpos].tx_nbits = op->addr.buswidth;
> +               spi_message_add_tail(&xfers[xferpos], &msg);
> +               xferpos++;
> +               totalxferlen += op->addr.nbytes;
> +       }
> +
> +       if (op->dummy.nbytes) {
> +               memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes);
> +               xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
> +               xfers[xferpos].len = op->dummy.nbytes;
> +               xfers[xferpos].tx_nbits = op->dummy.buswidth;
> +               spi_message_add_tail(&xfers[xferpos], &msg);
> +               xferpos++;
> +               totalxferlen += op->dummy.nbytes;
> +       }
> +
> +       if (op->data.nbytes) {
> +               if (op->data.dir == SPI_MEM_DATA_IN) {
> +                       xfers[xferpos].rx_buf = op->data.buf.in;
> +                       xfers[xferpos].rx_nbits = op->data.buswidth;
> +               } else {
> +                       xfers[xferpos].tx_buf = op->data.buf.out;
> +                       xfers[xferpos].tx_nbits = op->data.buswidth;
> +               }
> +
> +               xfers[xferpos].len = op->data.nbytes;
> +               spi_message_add_tail(&xfers[xferpos], &msg);
> +               xferpos++;
> +               totalxferlen += op->data.nbytes;
> +       }
> +
> +       ret = spi_sync(mem->spi, &msg);
> +
> +       kfree(tmpbuf);
> +
> +       if (ret)
> +               return ret;
> +
> +       if (msg.actual_length != totalxferlen)
> +               return -EIO;
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_exec_op);
> +
> +/**
> + * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
> + *                           match controller limitations
> + * @mem: the SPI memory
> + * @op: the operation to adjust
> + *
> + * Some controllers have FIFO limitations and must split a data transfer
> + * operation into multiple ones, others require a specific alignment for
> + * optimized accesses. This function allows SPI mem drivers to split a single
> + * operation into multiple sub-operations when required.
> + *
> + * Return: a negative error code if the controller can't properly adjust @op,
> + *        0 otherwise. Note that @op->data.nbytes will be updated if @op
> + *        can't be handled in a single step.
> + */
> +int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
> +{
> +       struct spi_controller *ctlr = mem->spi->controller;
> +
> +       if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
> +               return ctlr->mem_ops->adjust_op_size(mem, op);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
> +
> +static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
> +{
> +       return container_of(drv, struct spi_mem_driver, spidrv.driver);
> +}
> +
> +static int spi_mem_probe(struct spi_device *spi)
> +{
> +       struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +       struct spi_mem *mem;
> +
> +       mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL);
> +       if (!mem)
> +               return -ENOMEM;
> +
> +       mem->spi = spi;
> +       spi_set_drvdata(spi, mem);
> +
> +       return memdrv->probe(mem);
> +}
> +
> +static int spi_mem_remove(struct spi_device *spi)
> +{
> +       struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +       struct spi_mem *mem = spi_get_drvdata(spi);
> +
> +       if (memdrv->remove)
> +               return memdrv->remove(mem);
> +
> +       return 0;
> +}
> +
> +static void spi_mem_shutdown(struct spi_device *spi)
> +{
> +       struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +       struct spi_mem *mem = spi_get_drvdata(spi);
> +
> +       if (memdrv->shutdown)
> +               memdrv->shutdown(mem);
> +}
> +
> +/**
> + * spi_mem_driver_register_with_owner() - Register a SPI memory driver
> + * @memdrv: the SPI memory driver to register
> + * @owner: the owner of this driver
> + *
> + * Registers a SPI memory driver.
> + *
> + * Return: 0 in case of success, a negative error core otherwise.
> + */
> +
> +int spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv,
> +                                      struct module *owner)
> +{
> +       memdrv->spidrv.probe = spi_mem_probe;
> +       memdrv->spidrv.remove = spi_mem_remove;
> +       memdrv->spidrv.shutdown = spi_mem_shutdown;
> +
> +       return __spi_register_driver(owner, &memdrv->spidrv);
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner);
> +
> +/**
> + * spi_mem_driver_unregister_with_owner() - Unregister a SPI memory driver
> + * @memdrv: the SPI memory driver to unregister
> + *
> + * Unregisters a SPI memory driver.
> + */
> +void spi_mem_driver_unregister(struct spi_mem_driver *memdrv)
> +{
> +       spi_unregister_driver(&memdrv->spidrv);
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_driver_unregister);
> +
>  /*-------------------------------------------------------------------------*/
>
>  #if IS_ENABLED(CONFIG_OF_DYNAMIC)
> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> index 7b2170bfd6e7..af3c4ac62b55 100644
> --- a/include/linux/spi/spi.h
> +++ b/include/linux/spi/spi.h
> @@ -27,6 +27,7 @@ struct property_entry;
>  struct spi_controller;
>  struct spi_transfer;
>  struct spi_flash_read_message;
> +struct spi_controller_mem_ops;
>
>  /*
>   * INTERFACES between SPI master-side drivers and SPI slave protocol handlers,
> @@ -376,6 +377,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
>   *                    transfer_one callback.
>   * @handle_err: the subsystem calls the driver to handle an error that occurs
>   *             in the generic implementation of transfer_one_message().
> + * @mem_ops: optimized/dedicated operations for interactions with SPI memory.
> + *          This field is optional and should only be implemented if the
> + *          controller has native support for memory like operations.
>   * @unprepare_message: undo any work done by prepare_message().
>   * @slave_abort: abort the ongoing transfer request on an SPI slave controller
>   * @spi_flash_read: to support spi-controller hardwares that provide
> @@ -564,6 +568,9 @@ struct spi_controller {
>         void (*handle_err)(struct spi_controller *ctlr,
>                            struct spi_message *message);
>
> +       /* Optimized handlers for SPI memory-like operations. */
> +       const struct spi_controller_mem_ops *mem_ops;
> +
>         /* gpio chip select */
>         int                     *cs_gpios;
>
> @@ -1227,6 +1234,225 @@ int spi_flash_read(struct spi_device *spi,
>
>  /*---------------------------------------------------------------------------*/
>
> +/* SPI memory related definitions. */
> +
> +#define SPI_MEM_OP_CMD(__opcode, __buswidth)                   \
> +       {                                                       \
> +               .buswidth = __buswidth,                         \
> +               .opcode = __opcode,                             \
> +       }
> +
> +#define SPI_MEM_OP_ADDRS(__nbytes, __buf, __buswidth)          \
> +       {                                                       \
> +               .nbytes = __nbytes,                             \
> +               .buf = __buf,                                   \
> +               .buswidth = __buswidth,                         \
> +       }
> +
> +#define SPI_MEM_OP_NO_ADDRS    { }
> +
> +#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth)                 \
> +       {                                                       \
> +               .nbytes = __nbytes,                             \
> +               .buswidth = __buswidth,                         \
> +       }
> +
> +#define SPI_MEM_OP_NO_DUMMY    { }
> +
> +#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth)                \
> +       {                                                       \
> +               .dir = SPI_MEM_DATA_IN,                         \
> +               .nbytes = __nbytes,                             \
> +               .buf.in = __buf,                                \
> +               .buswidth = __buswidth,                         \
> +       }
> +
> +#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth)       \
> +       {                                                       \
> +               .dir = SPI_MEM_DATA_OUT,                        \
> +               .nbytes = __nbytes,                             \
> +               .buf.out = __buf,                               \
> +               .buswidth = __buswidth,                         \
> +       }
> +
> +#define SPI_MEM_OP_NO_DATA     { }
> +
> +/**
> + * enum spi_mem_data_dir - describes the direction of a SPI memory data
> + *                        transfer from the controller perspective
> + * @SPI_MEM_DATA_IN: data coming from the SPI memory
> + * @SPI_MEM_DATA_OUT: data sent the SPI memory
> + */
> +enum spi_mem_data_dir {
> +       SPI_MEM_DATA_IN,
> +       SPI_MEM_DATA_OUT,
> +};
> +
> +/**
> + * struct spi_mem_op - describes a SPI memory operation
> + * @cmd.buswidth: number of IO lines used to transmit the command
> + * @cmd.opcode: operation opcode
> + * @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.buf: buffer storing the address bytes
> + * @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
> + * @data.buswidth: number of IO lanes used to send/receive the data
> + * @data.dir: direction of the transfer
> + * @data.buf.in: input buffer
> + * @data.buf.out: output buffer
> + */
> +struct spi_mem_op {
> +       struct {
> +               u8 buswidth;
> +               u8 opcode;
> +       } cmd;
> +
> +       struct {
> +               u8 nbytes;
> +               u8 buswidth;
> +               const u8 *buf;
> +       } addr;
> +
> +       struct {
> +               u8 nbytes;
> +               u8 buswidth;
> +       } dummy;
> +
> +       struct {
> +               u8 buswidth;
> +               enum spi_mem_data_dir dir;
> +               unsigned int nbytes;
> +               /* buf.{in,out} must be DMA-able. */
> +               union {
> +                       void *in;
> +                       const void *out;
> +               } buf;
> +       } data;
> +};
> +
> +#define SPI_MEM_OP(__cmd, __addr, __dummy, __data)             \
> +       {                                                       \
> +               .cmd = __cmd,                                   \
> +               .addr = __addr,                                 \
> +               .dummy = __dummy,                               \
> +               .data = __data,                                 \
> +       }
> +
> +/**
> + * struct spi_mem - describes a SPI memory device
> + * @spi: the underlying SPI device
> + * @drvpriv: spi_mem_drviver private data
> + *
> + * Extra information that describe the SPI memory device and may be needed by
> + * the controller to properly handle this device should be placed here.
> + *
> + * One example would be the device size since some controller expose their SPI
> + * mem devices through a io-mapped region.
> + */
> +struct spi_mem {
> +       struct spi_device *spi;
> +       void *drvpriv;
> +};
> +
> +/**
> + * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
> + *                               device
> + * @mem: memory device
> + * @data: data to attach to the memory device
> + */
> +static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
> +{
> +       mem->drvpriv = data;
> +}
> +
> +/**
> + * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
> + *                               device
> + * @mem: memory device
> + *
> + * Return: the data attached to the mem device.
> + */
> +static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
> +{
> +       return mem->drvpriv;
> +}
> +
> +/**
> + * 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)
> + * @supports_op: check if an operation is supported by the controller
> + * @exec_op: execute a SPI memory operation
> + *
> + * 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.
> + */
> +struct spi_controller_mem_ops {
> +       int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
> +       bool (*supports_op)(struct spi_mem *mem,
> +                           const struct spi_mem_op *op);
> +       int (*exec_op)(struct spi_mem *mem,
> +                      const struct spi_mem_op *op);
> +};
> +
> +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
> +                                      const struct spi_mem_op *op,
> +                                      struct sg_table *sg);
> +
> +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> +                                         const struct spi_mem_op *op,
> +                                         struct sg_table *sg);
> +
> +int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
> +
> +bool spi_mem_supports_op(struct spi_mem *mem,
> +                        const struct spi_mem_op *op);
> +
> +int spi_mem_exec_op(struct spi_mem *mem,
> +                   const struct spi_mem_op *op);
> +
> +/**
> + * struct spi_mem_driver - SPI memory driver
> + * @spidrv: inherit from a SPI driver
> + * @probe: probe a SPI memory. Usually where detection/initialization takes
> + *        place
> + * @remove: remove a SPI memory
> + * @shutdown: take appropriate action when the system is shutdown
> + *
> + * This is just a thin wrapper around a spi_driver. The core takes care of
> + * allocating the spi_mem object and forwarding the probe/remove/shutdown
> + * request to the spi_mem_driver. The reason we use this wrapper is because
> + * we might have to stuff more information into the spi_mem struct to let
> + * SPI controllers know more about the SPI memory they interact with, and
> + * having this intermediate layer allows us to do that without adding more
> + * useless fields to the spi_device object.
> + */
> +struct spi_mem_driver {
> +       struct spi_driver spidrv;
> +       int (*probe)(struct spi_mem *mem);
> +       int (*remove)(struct spi_mem *mem);
> +       void (*shutdown)(struct spi_mem *mem);
> +};
> +
> +int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
> +                                      struct module *owner);
> +
> +#define spi_mem_driver_register(__drv)                                  \
> +       spi_mem_driver_register_with_owner(__drv, THIS_MODULE)
> +
> +void spi_mem_driver_unregister(struct spi_mem_driver *drv);
> +
> +#define module_spi_mem_driver(__drv)                                    \
> +       module_driver(__drv, spi_mem_driver_register,                   \
> +                     spi_mem_driver_unregister)
> +
> +/*---------------------------------------------------------------------------*/
> +
>  /*
>   * INTERFACE between board init code and SPI infrastructure.
>   *
> --
> 2.14.1
>

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-28  7:51       ` Peter Pan
@ 2018-02-28 12:25         ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-28 12:25 UTC (permalink / raw)
  To: Peter Pan
  Cc: Boris Brezillon, Yogesh Gaur, Vignesh R, Kamal Dasu,
	Richard Weinberger, linux-spi, Marek Vasut, Frieder Schrempf,
	Mark Brown, linux-mtd, Cyrille Pitchen, Rafał Miłecki,
	Sourav Poddar, Brian Norris, David Woodhouse

On Wed, 28 Feb 2018 15:51:29 +0800
Peter Pan <peterpansjtu@gmail.com> wrote:

> > +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> > +{
> > +       unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> > +       struct spi_controller *ctlr = mem->spi->controller;
> > +       struct spi_transfer xfers[4] = { };
> > +       struct spi_message msg;
> > +       u8 *tmpbuf;
> > +       int ret;
> > +
> > +       if (!spi_mem_supports_op(mem, op))
> > +               return -ENOTSUPP;
> > +
> > +       if (ctlr->mem_ops) {
> > +               if (ctlr->auto_runtime_pm) {
> > +                       ret = pm_runtime_get_sync(ctlr->dev.parent);
> > +                       if (ret < 0) {
> > +                               dev_err(&ctlr->dev,
> > +                                       "Failed to power device: %d\n",
> > +                                       ret);
> > +                               return ret;
> > +                       }
> > +               }
> > +
> > +               mutex_lock(&ctlr->bus_lock_mutex);
> > +               mutex_lock(&ctlr->io_mutex);
> > +               ret = ctlr->mem_ops->exec_op(mem, op);
> > +               mutex_unlock(&ctlr->io_mutex);
> > +               mutex_unlock(&ctlr->bus_lock_mutex);
> > +
> > +               if (ctlr->auto_runtime_pm)
> > +                       pm_runtime_put(ctlr->dev.parent);
> > +
> > +               /*
> > +                * Some controllers only optimize specific paths (typically the
> > +                * read path) and expect the core to use the regular SPI
> > +                * interface in these cases.
> > +                */
> > +               if (!ret || ret != -ENOTSUPP)
> > +                       return ret;
> > +       }
> > +
> > +       tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
> > +                    op->dummy.nbytes;  
> 
> Here you are using sizeof(op->cmd.opcode) while the code after this
> assumes opcode is u8(ie. "memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);")
> It may be confused.

Will use sizeof(op->cmd.opcode) everywhere then.

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-02-28 12:25         ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-02-28 12:25 UTC (permalink / raw)
  To: Peter Pan
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

On Wed, 28 Feb 2018 15:51:29 +0800
Peter Pan <peterpansjtu@gmail.com> wrote:

> > +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> > +{
> > +       unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> > +       struct spi_controller *ctlr = mem->spi->controller;
> > +       struct spi_transfer xfers[4] = { };
> > +       struct spi_message msg;
> > +       u8 *tmpbuf;
> > +       int ret;
> > +
> > +       if (!spi_mem_supports_op(mem, op))
> > +               return -ENOTSUPP;
> > +
> > +       if (ctlr->mem_ops) {
> > +               if (ctlr->auto_runtime_pm) {
> > +                       ret = pm_runtime_get_sync(ctlr->dev.parent);
> > +                       if (ret < 0) {
> > +                               dev_err(&ctlr->dev,
> > +                                       "Failed to power device: %d\n",
> > +                                       ret);
> > +                               return ret;
> > +                       }
> > +               }
> > +
> > +               mutex_lock(&ctlr->bus_lock_mutex);
> > +               mutex_lock(&ctlr->io_mutex);
> > +               ret = ctlr->mem_ops->exec_op(mem, op);
> > +               mutex_unlock(&ctlr->io_mutex);
> > +               mutex_unlock(&ctlr->bus_lock_mutex);
> > +
> > +               if (ctlr->auto_runtime_pm)
> > +                       pm_runtime_put(ctlr->dev.parent);
> > +
> > +               /*
> > +                * Some controllers only optimize specific paths (typically the
> > +                * read path) and expect the core to use the regular SPI
> > +                * interface in these cases.
> > +                */
> > +               if (!ret || ret != -ENOTSUPP)
> > +                       return ret;
> > +       }
> > +
> > +       tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
> > +                    op->dummy.nbytes;  
> 
> Here you are using sizeof(op->cmd.opcode) while the code after this
> assumes opcode is u8(ie. "memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);")
> It may be confused.

Will use sizeof(op->cmd.opcode) everywhere then.

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-02-05 23:21     ` Boris Brezillon
@ 2018-03-04 21:15       ` Cyrille Pitchen
  -1 siblings, 0 replies; 92+ messages in thread
From: Cyrille Pitchen @ 2018-03-04 21:15 UTC (permalink / raw)
  To: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, linux-mtd, Mark Brown,
	linux-spi
  Cc: Yogesh Gaur, Vignesh R, Kamal Dasu, Peter Pan, Frieder Schrempf,
	Rafał Miłecki, Sourav Poddar

Hi Boris,

Le 06/02/2018 à 00:21, Boris Brezillon a écrit :
> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> 
> Some controllers are exposing high-level interfaces to access various
> kind of SPI memories. Unfortunately they do not fit in the current
> spi_controller model and usually have drivers placed in
> drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> memories in general.
> 
> This is an attempt at defining a SPI memory interface which works for
> all kinds of SPI memories (NORs, NANDs, SRAMs).
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> ---
>  drivers/spi/spi.c       | 423 +++++++++++++++++++++++++++++++++++++++++++++++-
>  include/linux/spi/spi.h | 226 ++++++++++++++++++++++++++
>  2 files changed, 646 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index b33a727a0158..57bc540a0521 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -2057,6 +2057,24 @@ static int of_spi_register_master(struct spi_controller *ctlr)
>  }
>  #endif
>  
> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> +{
> +	/*
> +	 * The controller can implement only the high-level SPI-memory like
> +	 * operations if it does not support regular SPI transfers.
> +	 */
> +	if (ctlr->mem_ops) {
> +		if (!ctlr->mem_ops->supports_op ||
> +		    !ctlr->mem_ops->exec_op)
> +			return -EINVAL;
> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> +		   !ctlr->transfer_one_message) {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  /**
>   * spi_register_controller - register SPI master or slave controller
>   * @ctlr: initialized master, originally from spi_alloc_master() or
> @@ -2090,6 +2108,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>  	if (!dev)
>  		return -ENODEV;
>  
> +	/*
> +	 * Make sure all necessary hooks are implemented before registering
> +	 * the SPI controller.
> +	 */
> +	status = spi_controller_check_ops(ctlr);
> +	if (status)
> +		return status;
> +
>  	if (!spi_controller_is_slave(ctlr)) {
>  		status = of_spi_register_master(ctlr);
>  		if (status)
> @@ -2155,10 +2181,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>  			spi_controller_is_slave(ctlr) ? "slave" : "master",
>  			dev_name(&ctlr->dev));
>  
> -	/* If we're using a queued driver, start the queue */
> -	if (ctlr->transfer)
> +	/*
> +	 * If we're using a queued driver, start the queue. Note that we don't
> +	 * need the queueing logic if the driver is only supporting high-level
> +	 * memory operations.
> +	 */
> +	if (ctlr->transfer) {
>  		dev_info(dev, "controller is unqueued, this is deprecated\n");
> -	else {
> +	} else if (ctlr->transfer_one || ctlr->transfer_one_message) {
>  		status = spi_controller_initialize_queue(ctlr);
>  		if (status) {
>  			device_del(&ctlr->dev);
> @@ -2893,6 +2923,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
>  {
>  	struct spi_controller *ctlr = spi->controller;
>  
> +	/*
> +	 * Some controllers do not support doing regular SPI transfers. Return
> +	 * ENOTSUPP when this is the case.
> +	 */
> +	if (!ctlr->transfer)
> +		return -ENOTSUPP;
> +
>  	message->spi = spi;
>  
>  	SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_async);
> @@ -3321,6 +3358,386 @@ int spi_write_then_read(struct spi_device *spi,
>  }
>  EXPORT_SYMBOL_GPL(spi_write_then_read);
>  
> +/**
> + * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a
> + *					  memory operation
> + * @ctlr: the SPI controller requesting this dma_map()
> + * @op: the memory operation containing the buffer to map
> + * @sgt: a pointer to a non-initialized sg_table that will be filled by this
> + *	 function
> + *
> + * Some controllers might want to do DMA on the data buffer embedded in @op.
> + * This helper prepares everything for you and provides a ready-to-use
> + * sg_table. This function is not intended to be called from spi drivers.
> + * Only SPI controller drivers should use it.
> + * Note that the caller must ensure the memory region pointed by
> + * op->data.buf.{in,out} is DMA-able before calling this function.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
> +				       const struct spi_mem_op *op,
> +				       struct sg_table *sgt)
> +{
> +	struct device *dmadev;
> +
> +	if (!op->data.nbytes)
> +		return -EINVAL;
> +
> +	if (op->data.dir == SPI_MEM_DATA_OUT)
> +		dmadev = ctlr->dma_tx ?
> +			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +	else
> +		dmadev = ctlr->dma_rx ?
> +			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
> +
> +	if (!dmadev)
> +		return -EINVAL;
> +
> +	return spi_map_buf(ctlr, dmadev, sgt, op->data.buf.in, op->data.nbytes,
> +			   op->data.dir == SPI_MEM_DATA_IN ?
> +			   DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL_GPL(spi_controller_dma_map_mem_op_data);
> +
> +/**
> + * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a
> + *					    memory operation
> + * @ctlr: the SPI controller requesting this dma_unmap()
> + * @op: the memory operation containing the buffer to unmap
> + * @sgt: a pointer to an sg_table previously initialized by
> + *	 spi_controller_dma_map_mem_op_data()
> + *
> + * Some controllers might want to do DMA on the data buffer embedded in @op.
> + * This helper prepares things so that the CPU can access the
> + * op->data.buf.{in,out} buffer again.
> + *
> + * This function is not intended to be called from spi drivers. Only SPI
> + * controller drivers should use it.
> + *
> + * This function should be called after the DMA operation has finished an is
> + * only valid if the previous spi_controller_dma_map_mem_op_data() has returned
> + * 0.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> +					  const struct spi_mem_op *op,
> +					  struct sg_table *sgt)
> +{
> +	struct device *dmadev;
> +
> +	if (!op->data.nbytes)
> +		return;
> +
> +	if (op->data.dir == SPI_MEM_DATA_OUT)
> +		dmadev = ctlr->dma_tx ?
> +			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +	else
> +		dmadev = ctlr->dma_rx ?
> +			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
> +
> +	spi_unmap_buf(ctlr, dmadev, sgt,
> +		      op->data.dir == SPI_MEM_DATA_IN ?
> +		      DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data);
> +
> +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
> +{
> +	u32 mode = mem->spi->mode;
> +
> +	switch (buswidth) {
> +	case 1:
> +		return 0;
> +
> +	case 2:
> +		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> +		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))

Some SPI (flash) controller may support Quad protocols but no Duals
protocols at all. Hence you should only test SPI_{R,T]X_DUAL.

Anyway, I think the function should completely be removed because
SPI_{R,T}X_{DUAL,QUAD} flags should be deprecated.

They were fine when introduced long time ago when Quad SPI flashes only
supported Fast Read 1-1-4 and maybe few of them Page Program 1-1-4.

However for many years now, Quad SPI flashes support far much SPI protocols
and commands. For instance:
Fast Read 1-1-4
Fast Read 1-4-4
(Fast Read 4-4-4)
Page Program 1-1-4
Page Program 1-4-4
Page Program 4-4-4

Fast Read x-y-z means that:
- the op code is transfered (tx) on x I/O line(s)
- the address, mode and dummies are transfered (tx) on y I/O line(s)
- the data are received (rx) on z I/O line(s)

Page Program x-y-z stands for:
- the op code being transfered (tx) on x I/O line(s)
- the address is transfered (tx) on y I/O line(s)
the data are transfered (tx) on z I/O line(s)

Then some SPI flash controllers and/or memories may support Fast Read 1-4-4
but not Page Program 1-1-4 whereas others may support Page Program 1-1-4
but not Fast Read 1-4-4.

So using only SPI_{R,T}X_{DUAL,QUAD} flags, how do we make the difference
between support of Fast Read 1-4-4 and Page Program 1-1-4 or between
Fast Read 1-1-4 and Fast Read 1-4-4 ?
Indeed some latest (Cypress) memories only support Fast Read 1-4-4 but no
longer Fast Read 1-1-4.

I guess we'd rather use something close to the SNOR_HWCAPS_* macros as
defined in include/linux/mtd/spi-nor.h

SPI_{R,T}X_{DUAL,QUAD} flags are no longer suited to SPI flash memories for
a long time now.


> +			return 0;
> +
> +		break;
> +
> +	case 4:
> +		if ((tx && (mode & SPI_TX_QUAD)) ||
> +		    (!tx && (mode & SPI_RX_QUAD)))
> +			return 0;
> +
> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	return -ENOTSUPP;
> +}
> +
> +/**
> + * spi_mem_supports_op() - Check if a memory device and the controller it is
> + *			   connected to support a specific memory operation
> + * @mem: the SPI memory
> + * @op: the memory operation to check
> + *
> + * Some controllers are only supporting Single or Dual IOs, others might only
> + * support specific opcodes, or it can even be that the controller and device
> + * both support Quad IOs but the hardware prevents you from using it because
> + * only 2 IO lines are connected.
> + *
> + * This function checks whether a specific operation is supported.
> + *
> + * Return: true if @op is supported, false otherwise.
> + */
> +bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +	struct spi_controller *ctlr = mem->spi->controller;
> +
> +	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
> +		return false;
> +
> +	if (op->addr.nbytes &&
> +	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
> +		return false;
> +
> +	if (op->dummy.nbytes &&
> +	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
> +		return false;
> +
> +	if (op->data.nbytes &&
> +	    spi_check_buswidth_req(mem, op->data.buswidth,
> +				   op->data.dir == SPI_MEM_DATA_IN ?
> +				   false : true))
> +		return false;

You should remove the 4 tests and calls of spi_check_buswidth_req() above.
Based on spi_controller_check_ops(), when ctrl->mem_ops is not NULL,
mem_ops->supports_op() must be provided, this hook is mandatory.

So call this latest hook only, has done just below. You may want to keep the 4
tests above and spi_check_buswidth_req() but then move them in a
stand-alone function that could be used as a default implementation of
mem_ops->supports_op() if you want to allow some SPI controller drivers not
to implement ->supports_op() by their own.

However it should be considered a transitionnal and at some point I think
SPI_{R,T}X_{DUAL,QUAD} flags should just be removed because many
developers submitting patches to spi-nor have often been confused by how
properly use those flags in their drivers.

Till then, if you removed the 4 tests above, at least new SPI flash
controller drivers won't have to set some odd combination of flags in
spi->mode to make their support of SPI flash memories work as they want.

> +
> +	if (ctlr->mem_ops)
> +		return ctlr->mem_ops->supports_op(mem, op);
> +
> +	return true;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_supports_op);
> +
> +/**
> + * spi_mem_exec_op() - Execute a memory operation
> + * @mem: the SPI memory
> + * @op: the memory operation to execute
> + *
> + * Executes a memory operation.
> + *
> + * This function first checks that @op is supported and then tries to execute
> + * it.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> +	struct spi_controller *ctlr = mem->spi->controller;
> +	struct spi_transfer xfers[4] = { };
> +	struct spi_message msg;
> +	u8 *tmpbuf;
> +	int ret;
> +
> +	if (!spi_mem_supports_op(mem, op))
> +		return -ENOTSUPP;
> +
> +	if (ctlr->mem_ops) {
> +		if (ctlr->auto_runtime_pm) {
> +			ret = pm_runtime_get_sync(ctlr->dev.parent);
> +			if (ret < 0) {
> +				dev_err(&ctlr->dev,
> +					"Failed to power device: %d\n",
> +					ret);
> +				return ret;
> +			}
> +		}
> +
> +		mutex_lock(&ctlr->bus_lock_mutex);
> +		mutex_lock(&ctlr->io_mutex);
> +		ret = ctlr->mem_ops->exec_op(mem, op);
> +		mutex_unlock(&ctlr->io_mutex);
> +		mutex_unlock(&ctlr->bus_lock_mutex);
> +
> +		if (ctlr->auto_runtime_pm)
> +			pm_runtime_put(ctlr->dev.parent);
> +
> +		/*
> +		 * Some controllers only optimize specific paths (typically the
> +		 * read path) and expect the core to use the regular SPI
> +		 * interface in these cases.
> +		 */
> +		if (!ret || ret != -ENOTSUPP)
> +			return ret;
> +	}
> +
> +	tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
> +		     op->dummy.nbytes;
> +
> +	/*
> +	 * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
> +	 * we're guaranteed that this buffer is DMA-able, as required by the
> +	 * SPI layer.
> +	 */
> +	tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
> +	if (!tmpbuf)
> +		return -ENOMEM;
> +
> +	spi_message_init(&msg);
> +
> +	tmpbuf[0] = op->cmd.opcode;
> +	xfers[xferpos].tx_buf = tmpbuf;
> +	xfers[xferpos].len = sizeof(op->cmd.opcode);
> +	xfers[xferpos].tx_nbits = op->cmd.buswidth;
> +	spi_message_add_tail(&xfers[xferpos], &msg);
> +	xferpos++;
> +	totalxferlen++;
> +
> +	if (op->addr.nbytes) {
> +		memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);
> +		xfers[xferpos].tx_buf = tmpbuf + 1;
> +		xfers[xferpos].len = op->addr.nbytes;
> +		xfers[xferpos].tx_nbits = op->addr.buswidth;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->addr.nbytes;
> +	}
> +
> +	if (op->dummy.nbytes) {
> +		memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes);
> +		xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
> +		xfers[xferpos].len = op->dummy.nbytes;
> +		xfers[xferpos].tx_nbits = op->dummy.buswidth;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->dummy.nbytes;
> +	}
> +
> +	if (op->data.nbytes) {
> +		if (op->data.dir == SPI_MEM_DATA_IN) {
> +			xfers[xferpos].rx_buf = op->data.buf.in;
> +			xfers[xferpos].rx_nbits = op->data.buswidth;
> +		} else {
> +			xfers[xferpos].tx_buf = op->data.buf.out;
> +			xfers[xferpos].tx_nbits = op->data.buswidth;
> +		}
> +
> +		xfers[xferpos].len = op->data.nbytes;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->data.nbytes;
> +	}
> +
> +	ret = spi_sync(mem->spi, &msg);
> +
> +	kfree(tmpbuf);
> +
> +	if (ret)
> +		return ret;
> +
> +	if (msg.actual_length != totalxferlen)
> +		return -EIO;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_exec_op);
> +
> +/**
> + * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
> + *			      match controller limitations
> + * @mem: the SPI memory
> + * @op: the operation to adjust
> + *
> + * Some controllers have FIFO limitations and must split a data transfer
> + * operation into multiple ones, others require a specific alignment for
> + * optimized accesses. This function allows SPI mem drivers to split a single
> + * operation into multiple sub-operations when required.
> + *
> + * Return: a negative error code if the controller can't properly adjust @op,
> + *	   0 otherwise. Note that @op->data.nbytes will be updated if @op
> + *	   can't be handled in a single step.
> + */
> +int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
> +{
> +	struct spi_controller *ctlr = mem->spi->controller;
> +
> +	if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
> +		return ctlr->mem_ops->adjust_op_size(mem, op);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
> +
> +static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
> +{
> +	return container_of(drv, struct spi_mem_driver, spidrv.driver);
> +}
> +
> +static int spi_mem_probe(struct spi_device *spi)
> +{
> +	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +	struct spi_mem *mem;
> +
> +	mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL);
> +	if (!mem)
> +		return -ENOMEM;
> +
> +	mem->spi = spi;
> +	spi_set_drvdata(spi, mem);
> +
> +	return memdrv->probe(mem);
> +}
> +
> +static int spi_mem_remove(struct spi_device *spi)
> +{
> +	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +	struct spi_mem *mem = spi_get_drvdata(spi);
> +
> +	if (memdrv->remove)
> +		return memdrv->remove(mem);
> +
> +	return 0;
> +}
> +
> +static void spi_mem_shutdown(struct spi_device *spi)
> +{
> +	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +	struct spi_mem *mem = spi_get_drvdata(spi);
> +
> +	if (memdrv->shutdown)
> +		memdrv->shutdown(mem);
> +}
> +
> +/**
> + * spi_mem_driver_register_with_owner() - Register a SPI memory driver
> + * @memdrv: the SPI memory driver to register
> + * @owner: the owner of this driver
> + *
> + * Registers a SPI memory driver.
> + *
> + * Return: 0 in case of success, a negative error core otherwise.
> + */
> +
> +int spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv,
> +				       struct module *owner)
> +{
> +	memdrv->spidrv.probe = spi_mem_probe;
> +	memdrv->spidrv.remove = spi_mem_remove;
> +	memdrv->spidrv.shutdown = spi_mem_shutdown;
> +
> +	return __spi_register_driver(owner, &memdrv->spidrv);
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner);
> +
> +/**
> + * spi_mem_driver_unregister_with_owner() - Unregister a SPI memory driver
> + * @memdrv: the SPI memory driver to unregister
> + *
> + * Unregisters a SPI memory driver.
> + */
> +void spi_mem_driver_unregister(struct spi_mem_driver *memdrv)
> +{
> +	spi_unregister_driver(&memdrv->spidrv);
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_driver_unregister);
> +
>  /*-------------------------------------------------------------------------*/
>  
>  #if IS_ENABLED(CONFIG_OF_DYNAMIC)
> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> index 7b2170bfd6e7..af3c4ac62b55 100644
> --- a/include/linux/spi/spi.h
> +++ b/include/linux/spi/spi.h
> @@ -27,6 +27,7 @@ struct property_entry;
>  struct spi_controller;
>  struct spi_transfer;
>  struct spi_flash_read_message;
> +struct spi_controller_mem_ops;
>  
>  /*
>   * INTERFACES between SPI master-side drivers and SPI slave protocol handlers,
> @@ -376,6 +377,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
>   *                    transfer_one callback.
>   * @handle_err: the subsystem calls the driver to handle an error that occurs
>   *		in the generic implementation of transfer_one_message().
> + * @mem_ops: optimized/dedicated operations for interactions with SPI memory.
> + *	     This field is optional and should only be implemented if the
> + *	     controller has native support for memory like operations.
>   * @unprepare_message: undo any work done by prepare_message().
>   * @slave_abort: abort the ongoing transfer request on an SPI slave controller
>   * @spi_flash_read: to support spi-controller hardwares that provide
> @@ -564,6 +568,9 @@ struct spi_controller {
>  	void (*handle_err)(struct spi_controller *ctlr,
>  			   struct spi_message *message);
>  
> +	/* Optimized handlers for SPI memory-like operations. */
> +	const struct spi_controller_mem_ops *mem_ops;
> +
>  	/* gpio chip select */
>  	int			*cs_gpios;
>  
> @@ -1227,6 +1234,225 @@ int spi_flash_read(struct spi_device *spi,
>  
>  /*---------------------------------------------------------------------------*/
>  
> +/* SPI memory related definitions. */
> +
> +#define SPI_MEM_OP_CMD(__opcode, __buswidth)			\
> +	{							\
> +		.buswidth = __buswidth,				\
> +		.opcode = __opcode,				\
> +	}
> +
> +#define SPI_MEM_OP_ADDRS(__nbytes, __buf, __buswidth)		\
> +	{							\
> +		.nbytes = __nbytes,				\
> +		.buf = __buf,					\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_NO_ADDRS	{ }
> +
> +#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth)			\
> +	{							\
> +		.nbytes = __nbytes,				\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_NO_DUMMY	{ }
> +
> +#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth)		\
> +	{							\
> +		.dir = SPI_MEM_DATA_IN,				\
> +		.nbytes = __nbytes,				\
> +		.buf.in = __buf,				\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth)	\
> +	{							\
> +		.dir = SPI_MEM_DATA_OUT,			\
> +		.nbytes = __nbytes,				\
> +		.buf.out = __buf,				\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_NO_DATA	{ }
> +
> +/**
> + * enum spi_mem_data_dir - describes the direction of a SPI memory data
> + *			   transfer from the controller perspective
> + * @SPI_MEM_DATA_IN: data coming from the SPI memory
> + * @SPI_MEM_DATA_OUT: data sent the SPI memory
> + */
> +enum spi_mem_data_dir {
> +	SPI_MEM_DATA_IN,
> +	SPI_MEM_DATA_OUT,
> +};
> +
> +/**
> + * struct spi_mem_op - describes a SPI memory operation
> + * @cmd.buswidth: number of IO lines used to transmit the command
> + * @cmd.opcode: operation opcode
> + * @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.buf: buffer storing the address bytes
> + * @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
> + * @data.buswidth: number of IO lanes used to send/receive the data
> + * @data.dir: direction of the transfer
> + * @data.buf.in: input buffer
> + * @data.buf.out: output buffer
> + */
> +struct spi_mem_op {
> +	struct {
> +		u8 buswidth;
> +		u8 opcode;
> +	} cmd;
> +
> +	struct {
> +		u8 nbytes;
> +		u8 buswidth;
> +		const u8 *buf;

For the address value, I would rather use some loff_t type, instead of
const u8 *. 
Actually, most (if not all) SPI flash controllers have some hardware
register to set the address value of the SPI flash command to be
sent. Hence having this value directly in the right format would avoid to
convert. AFAIK, only m25p80 needs to convert from loff_t to u8 * and only
when using the regular SPI API, ie spi_sync(), as the 'struct
spi_flash_read_messag' already uses some integral type too.


> +	} addr;
> +
> +	struct {
> +		u8 nbytes;
> +		u8 buswidth;
> +	} dummy;
> +
> +	struct {
> +		u8 buswidth;
> +		enum spi_mem_data_dir dir;
> +		unsigned int nbytes;
> +		/* buf.{in,out} must be DMA-able. */
> +		union {
> +			void *in;
> +			const void *out;
> +		} buf;
> +	} data;

Also, you should add another member in this structure to set the 'type' of
operation for the SPI flash command:
- register read
- register write
- memory read
- memory write
- memory erase
[- SFDP read ? ]
[- OTP read/write ? ]

Indeed, some SPI flash controller drivers need this information.

For instance the Atmel QSPI controller has some optional feature that allow
to scramble data on the fly from/to the (Q)SPI memory. Hence the scrambling
should be enabled for memory {read,write} (Fast Read, Page Programm)
commands but disabled for register {read,write} commands (Read ID, Write
Enable, Read/Write Status Register, ...).

SPI flash controller drivers *should not* have to check the SPI flash command
instruction op code to guess what kind of operation is being performed.

Another example:
Some (Q)SPI flash controllers map the the SPI flash memory into the system
address space then perform some "memcpy-like" operations to access this SPI
flash memory whereas they rely on reguler register to handle SPI flash
commands reading from or writing into the internal registers fo the SPI
flash. So those controller too need to make the difference between register
and memory operations.

Best regards,

Cyrille

> +};
> +
> +#define SPI_MEM_OP(__cmd, __addr, __dummy, __data)		\
> +	{							\
> +		.cmd = __cmd,					\
> +		.addr = __addr,					\
> +		.dummy = __dummy,				\
> +		.data = __data,					\
> +	}
> +
> +/**
> + * struct spi_mem - describes a SPI memory device
> + * @spi: the underlying SPI device
> + * @drvpriv: spi_mem_drviver private data
> + *
> + * Extra information that describe the SPI memory device and may be needed by
> + * the controller to properly handle this device should be placed here.
> + *
> + * One example would be the device size since some controller expose their SPI
> + * mem devices through a io-mapped region.
> + */
> +struct spi_mem {
> +	struct spi_device *spi;
> +	void *drvpriv;
> +};
> +
> +/**
> + * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
> + *				  device
> + * @mem: memory device
> + * @data: data to attach to the memory device
> + */
> +static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
> +{
> +	mem->drvpriv = data;
> +}
> +
> +/**
> + * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
> + *				  device
> + * @mem: memory device
> + *
> + * Return: the data attached to the mem device.
> + */
> +static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
> +{
> +	return mem->drvpriv;
> +}
> +
> +/**
> + * 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)
> + * @supports_op: check if an operation is supported by the controller
> + * @exec_op: execute a SPI memory operation
> + *
> + * 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.
> + */
> +struct spi_controller_mem_ops {
> +	int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
> +	bool (*supports_op)(struct spi_mem *mem,
> +			    const struct spi_mem_op *op);
> +	int (*exec_op)(struct spi_mem *mem,
> +		       const struct spi_mem_op *op);
> +};
> +
> +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
> +				       const struct spi_mem_op *op,
> +				       struct sg_table *sg);
> +
> +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> +					  const struct spi_mem_op *op,
> +					  struct sg_table *sg);
> +
> +int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
> +
> +bool spi_mem_supports_op(struct spi_mem *mem,
> +			 const struct spi_mem_op *op);
> +
> +int spi_mem_exec_op(struct spi_mem *mem,
> +		    const struct spi_mem_op *op);
> +
> +/**
> + * struct spi_mem_driver - SPI memory driver
> + * @spidrv: inherit from a SPI driver
> + * @probe: probe a SPI memory. Usually where detection/initialization takes
> + *	   place
> + * @remove: remove a SPI memory
> + * @shutdown: take appropriate action when the system is shutdown
> + *
> + * This is just a thin wrapper around a spi_driver. The core takes care of
> + * allocating the spi_mem object and forwarding the probe/remove/shutdown
> + * request to the spi_mem_driver. The reason we use this wrapper is because
> + * we might have to stuff more information into the spi_mem struct to let
> + * SPI controllers know more about the SPI memory they interact with, and
> + * having this intermediate layer allows us to do that without adding more
> + * useless fields to the spi_device object.
> + */
> +struct spi_mem_driver {
> +	struct spi_driver spidrv;
> +	int (*probe)(struct spi_mem *mem);
> +	int (*remove)(struct spi_mem *mem);
> +	void (*shutdown)(struct spi_mem *mem);
> +};
> +
> +int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
> +				       struct module *owner);
> +
> +#define spi_mem_driver_register(__drv)                                  \
> +	spi_mem_driver_register_with_owner(__drv, THIS_MODULE)
> +
> +void spi_mem_driver_unregister(struct spi_mem_driver *drv);
> +
> +#define module_spi_mem_driver(__drv)                                    \
> +	module_driver(__drv, spi_mem_driver_register,                   \
> +		      spi_mem_driver_unregister)
> +
> +/*---------------------------------------------------------------------------*/
> +
>  /*
>   * INTERFACE between board init code and SPI infrastructure.
>   *
> 


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-03-04 21:15       ` Cyrille Pitchen
  0 siblings, 0 replies; 92+ messages in thread
From: Cyrille Pitchen @ 2018-03-04 21:15 UTC (permalink / raw)
  To: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, linux-mtd, Mark Brown,
	linux-spi
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

Hi Boris,

Le 06/02/2018 à 00:21, Boris Brezillon a écrit :
> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> 
> Some controllers are exposing high-level interfaces to access various
> kind of SPI memories. Unfortunately they do not fit in the current
> spi_controller model and usually have drivers placed in
> drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
> memories in general.
> 
> This is an attempt at defining a SPI memory interface which works for
> all kinds of SPI memories (NORs, NANDs, SRAMs).
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> ---
>  drivers/spi/spi.c       | 423 +++++++++++++++++++++++++++++++++++++++++++++++-
>  include/linux/spi/spi.h | 226 ++++++++++++++++++++++++++
>  2 files changed, 646 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index b33a727a0158..57bc540a0521 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -2057,6 +2057,24 @@ static int of_spi_register_master(struct spi_controller *ctlr)
>  }
>  #endif
>  
> +static int spi_controller_check_ops(struct spi_controller *ctlr)
> +{
> +	/*
> +	 * The controller can implement only the high-level SPI-memory like
> +	 * operations if it does not support regular SPI transfers.
> +	 */
> +	if (ctlr->mem_ops) {
> +		if (!ctlr->mem_ops->supports_op ||
> +		    !ctlr->mem_ops->exec_op)
> +			return -EINVAL;
> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> +		   !ctlr->transfer_one_message) {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  /**
>   * spi_register_controller - register SPI master or slave controller
>   * @ctlr: initialized master, originally from spi_alloc_master() or
> @@ -2090,6 +2108,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>  	if (!dev)
>  		return -ENODEV;
>  
> +	/*
> +	 * Make sure all necessary hooks are implemented before registering
> +	 * the SPI controller.
> +	 */
> +	status = spi_controller_check_ops(ctlr);
> +	if (status)
> +		return status;
> +
>  	if (!spi_controller_is_slave(ctlr)) {
>  		status = of_spi_register_master(ctlr);
>  		if (status)
> @@ -2155,10 +2181,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>  			spi_controller_is_slave(ctlr) ? "slave" : "master",
>  			dev_name(&ctlr->dev));
>  
> -	/* If we're using a queued driver, start the queue */
> -	if (ctlr->transfer)
> +	/*
> +	 * If we're using a queued driver, start the queue. Note that we don't
> +	 * need the queueing logic if the driver is only supporting high-level
> +	 * memory operations.
> +	 */
> +	if (ctlr->transfer) {
>  		dev_info(dev, "controller is unqueued, this is deprecated\n");
> -	else {
> +	} else if (ctlr->transfer_one || ctlr->transfer_one_message) {
>  		status = spi_controller_initialize_queue(ctlr);
>  		if (status) {
>  			device_del(&ctlr->dev);
> @@ -2893,6 +2923,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
>  {
>  	struct spi_controller *ctlr = spi->controller;
>  
> +	/*
> +	 * Some controllers do not support doing regular SPI transfers. Return
> +	 * ENOTSUPP when this is the case.
> +	 */
> +	if (!ctlr->transfer)
> +		return -ENOTSUPP;
> +
>  	message->spi = spi;
>  
>  	SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_async);
> @@ -3321,6 +3358,386 @@ int spi_write_then_read(struct spi_device *spi,
>  }
>  EXPORT_SYMBOL_GPL(spi_write_then_read);
>  
> +/**
> + * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a
> + *					  memory operation
> + * @ctlr: the SPI controller requesting this dma_map()
> + * @op: the memory operation containing the buffer to map
> + * @sgt: a pointer to a non-initialized sg_table that will be filled by this
> + *	 function
> + *
> + * Some controllers might want to do DMA on the data buffer embedded in @op.
> + * This helper prepares everything for you and provides a ready-to-use
> + * sg_table. This function is not intended to be called from spi drivers.
> + * Only SPI controller drivers should use it.
> + * Note that the caller must ensure the memory region pointed by
> + * op->data.buf.{in,out} is DMA-able before calling this function.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
> +				       const struct spi_mem_op *op,
> +				       struct sg_table *sgt)
> +{
> +	struct device *dmadev;
> +
> +	if (!op->data.nbytes)
> +		return -EINVAL;
> +
> +	if (op->data.dir == SPI_MEM_DATA_OUT)
> +		dmadev = ctlr->dma_tx ?
> +			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +	else
> +		dmadev = ctlr->dma_rx ?
> +			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
> +
> +	if (!dmadev)
> +		return -EINVAL;
> +
> +	return spi_map_buf(ctlr, dmadev, sgt, op->data.buf.in, op->data.nbytes,
> +			   op->data.dir == SPI_MEM_DATA_IN ?
> +			   DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL_GPL(spi_controller_dma_map_mem_op_data);
> +
> +/**
> + * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a
> + *					    memory operation
> + * @ctlr: the SPI controller requesting this dma_unmap()
> + * @op: the memory operation containing the buffer to unmap
> + * @sgt: a pointer to an sg_table previously initialized by
> + *	 spi_controller_dma_map_mem_op_data()
> + *
> + * Some controllers might want to do DMA on the data buffer embedded in @op.
> + * This helper prepares things so that the CPU can access the
> + * op->data.buf.{in,out} buffer again.
> + *
> + * This function is not intended to be called from spi drivers. Only SPI
> + * controller drivers should use it.
> + *
> + * This function should be called after the DMA operation has finished an is
> + * only valid if the previous spi_controller_dma_map_mem_op_data() has returned
> + * 0.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> +					  const struct spi_mem_op *op,
> +					  struct sg_table *sgt)
> +{
> +	struct device *dmadev;
> +
> +	if (!op->data.nbytes)
> +		return;
> +
> +	if (op->data.dir == SPI_MEM_DATA_OUT)
> +		dmadev = ctlr->dma_tx ?
> +			 ctlr->dma_tx->device->dev : ctlr->dev.parent;
> +	else
> +		dmadev = ctlr->dma_rx ?
> +			 ctlr->dma_rx->device->dev : ctlr->dev.parent;
> +
> +	spi_unmap_buf(ctlr, dmadev, sgt,
> +		      op->data.dir == SPI_MEM_DATA_IN ?
> +		      DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data);
> +
> +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
> +{
> +	u32 mode = mem->spi->mode;
> +
> +	switch (buswidth) {
> +	case 1:
> +		return 0;
> +
> +	case 2:
> +		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> +		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))

Some SPI (flash) controller may support Quad protocols but no Duals
protocols at all. Hence you should only test SPI_{R,T]X_DUAL.

Anyway, I think the function should completely be removed because
SPI_{R,T}X_{DUAL,QUAD} flags should be deprecated.

They were fine when introduced long time ago when Quad SPI flashes only
supported Fast Read 1-1-4 and maybe few of them Page Program 1-1-4.

However for many years now, Quad SPI flashes support far much SPI protocols
and commands. For instance:
Fast Read 1-1-4
Fast Read 1-4-4
(Fast Read 4-4-4)
Page Program 1-1-4
Page Program 1-4-4
Page Program 4-4-4

Fast Read x-y-z means that:
- the op code is transfered (tx) on x I/O line(s)
- the address, mode and dummies are transfered (tx) on y I/O line(s)
- the data are received (rx) on z I/O line(s)

Page Program x-y-z stands for:
- the op code being transfered (tx) on x I/O line(s)
- the address is transfered (tx) on y I/O line(s)
the data are transfered (tx) on z I/O line(s)

Then some SPI flash controllers and/or memories may support Fast Read 1-4-4
but not Page Program 1-1-4 whereas others may support Page Program 1-1-4
but not Fast Read 1-4-4.

So using only SPI_{R,T}X_{DUAL,QUAD} flags, how do we make the difference
between support of Fast Read 1-4-4 and Page Program 1-1-4 or between
Fast Read 1-1-4 and Fast Read 1-4-4 ?
Indeed some latest (Cypress) memories only support Fast Read 1-4-4 but no
longer Fast Read 1-1-4.

I guess we'd rather use something close to the SNOR_HWCAPS_* macros as
defined in include/linux/mtd/spi-nor.h

SPI_{R,T}X_{DUAL,QUAD} flags are no longer suited to SPI flash memories for
a long time now.


> +			return 0;
> +
> +		break;
> +
> +	case 4:
> +		if ((tx && (mode & SPI_TX_QUAD)) ||
> +		    (!tx && (mode & SPI_RX_QUAD)))
> +			return 0;
> +
> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	return -ENOTSUPP;
> +}
> +
> +/**
> + * spi_mem_supports_op() - Check if a memory device and the controller it is
> + *			   connected to support a specific memory operation
> + * @mem: the SPI memory
> + * @op: the memory operation to check
> + *
> + * Some controllers are only supporting Single or Dual IOs, others might only
> + * support specific opcodes, or it can even be that the controller and device
> + * both support Quad IOs but the hardware prevents you from using it because
> + * only 2 IO lines are connected.
> + *
> + * This function checks whether a specific operation is supported.
> + *
> + * Return: true if @op is supported, false otherwise.
> + */
> +bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +	struct spi_controller *ctlr = mem->spi->controller;
> +
> +	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
> +		return false;
> +
> +	if (op->addr.nbytes &&
> +	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
> +		return false;
> +
> +	if (op->dummy.nbytes &&
> +	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
> +		return false;
> +
> +	if (op->data.nbytes &&
> +	    spi_check_buswidth_req(mem, op->data.buswidth,
> +				   op->data.dir == SPI_MEM_DATA_IN ?
> +				   false : true))
> +		return false;

You should remove the 4 tests and calls of spi_check_buswidth_req() above.
Based on spi_controller_check_ops(), when ctrl->mem_ops is not NULL,
mem_ops->supports_op() must be provided, this hook is mandatory.

So call this latest hook only, has done just below. You may want to keep the 4
tests above and spi_check_buswidth_req() but then move them in a
stand-alone function that could be used as a default implementation of
mem_ops->supports_op() if you want to allow some SPI controller drivers not
to implement ->supports_op() by their own.

However it should be considered a transitionnal and at some point I think
SPI_{R,T}X_{DUAL,QUAD} flags should just be removed because many
developers submitting patches to spi-nor have often been confused by how
properly use those flags in their drivers.

Till then, if you removed the 4 tests above, at least new SPI flash
controller drivers won't have to set some odd combination of flags in
spi->mode to make their support of SPI flash memories work as they want.

> +
> +	if (ctlr->mem_ops)
> +		return ctlr->mem_ops->supports_op(mem, op);
> +
> +	return true;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_supports_op);
> +
> +/**
> + * spi_mem_exec_op() - Execute a memory operation
> + * @mem: the SPI memory
> + * @op: the memory operation to execute
> + *
> + * Executes a memory operation.
> + *
> + * This function first checks that @op is supported and then tries to execute
> + * it.
> + *
> + * Return: 0 in case of success, a negative error code otherwise.
> + */
> +int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> +	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
> +	struct spi_controller *ctlr = mem->spi->controller;
> +	struct spi_transfer xfers[4] = { };
> +	struct spi_message msg;
> +	u8 *tmpbuf;
> +	int ret;
> +
> +	if (!spi_mem_supports_op(mem, op))
> +		return -ENOTSUPP;
> +
> +	if (ctlr->mem_ops) {
> +		if (ctlr->auto_runtime_pm) {
> +			ret = pm_runtime_get_sync(ctlr->dev.parent);
> +			if (ret < 0) {
> +				dev_err(&ctlr->dev,
> +					"Failed to power device: %d\n",
> +					ret);
> +				return ret;
> +			}
> +		}
> +
> +		mutex_lock(&ctlr->bus_lock_mutex);
> +		mutex_lock(&ctlr->io_mutex);
> +		ret = ctlr->mem_ops->exec_op(mem, op);
> +		mutex_unlock(&ctlr->io_mutex);
> +		mutex_unlock(&ctlr->bus_lock_mutex);
> +
> +		if (ctlr->auto_runtime_pm)
> +			pm_runtime_put(ctlr->dev.parent);
> +
> +		/*
> +		 * Some controllers only optimize specific paths (typically the
> +		 * read path) and expect the core to use the regular SPI
> +		 * interface in these cases.
> +		 */
> +		if (!ret || ret != -ENOTSUPP)
> +			return ret;
> +	}
> +
> +	tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
> +		     op->dummy.nbytes;
> +
> +	/*
> +	 * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
> +	 * we're guaranteed that this buffer is DMA-able, as required by the
> +	 * SPI layer.
> +	 */
> +	tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
> +	if (!tmpbuf)
> +		return -ENOMEM;
> +
> +	spi_message_init(&msg);
> +
> +	tmpbuf[0] = op->cmd.opcode;
> +	xfers[xferpos].tx_buf = tmpbuf;
> +	xfers[xferpos].len = sizeof(op->cmd.opcode);
> +	xfers[xferpos].tx_nbits = op->cmd.buswidth;
> +	spi_message_add_tail(&xfers[xferpos], &msg);
> +	xferpos++;
> +	totalxferlen++;
> +
> +	if (op->addr.nbytes) {
> +		memcpy(tmpbuf + 1, op->addr.buf, op->addr.nbytes);
> +		xfers[xferpos].tx_buf = tmpbuf + 1;
> +		xfers[xferpos].len = op->addr.nbytes;
> +		xfers[xferpos].tx_nbits = op->addr.buswidth;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->addr.nbytes;
> +	}
> +
> +	if (op->dummy.nbytes) {
> +		memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes);
> +		xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
> +		xfers[xferpos].len = op->dummy.nbytes;
> +		xfers[xferpos].tx_nbits = op->dummy.buswidth;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->dummy.nbytes;
> +	}
> +
> +	if (op->data.nbytes) {
> +		if (op->data.dir == SPI_MEM_DATA_IN) {
> +			xfers[xferpos].rx_buf = op->data.buf.in;
> +			xfers[xferpos].rx_nbits = op->data.buswidth;
> +		} else {
> +			xfers[xferpos].tx_buf = op->data.buf.out;
> +			xfers[xferpos].tx_nbits = op->data.buswidth;
> +		}
> +
> +		xfers[xferpos].len = op->data.nbytes;
> +		spi_message_add_tail(&xfers[xferpos], &msg);
> +		xferpos++;
> +		totalxferlen += op->data.nbytes;
> +	}
> +
> +	ret = spi_sync(mem->spi, &msg);
> +
> +	kfree(tmpbuf);
> +
> +	if (ret)
> +		return ret;
> +
> +	if (msg.actual_length != totalxferlen)
> +		return -EIO;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_exec_op);
> +
> +/**
> + * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
> + *			      match controller limitations
> + * @mem: the SPI memory
> + * @op: the operation to adjust
> + *
> + * Some controllers have FIFO limitations and must split a data transfer
> + * operation into multiple ones, others require a specific alignment for
> + * optimized accesses. This function allows SPI mem drivers to split a single
> + * operation into multiple sub-operations when required.
> + *
> + * Return: a negative error code if the controller can't properly adjust @op,
> + *	   0 otherwise. Note that @op->data.nbytes will be updated if @op
> + *	   can't be handled in a single step.
> + */
> +int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
> +{
> +	struct spi_controller *ctlr = mem->spi->controller;
> +
> +	if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
> +		return ctlr->mem_ops->adjust_op_size(mem, op);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
> +
> +static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
> +{
> +	return container_of(drv, struct spi_mem_driver, spidrv.driver);
> +}
> +
> +static int spi_mem_probe(struct spi_device *spi)
> +{
> +	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +	struct spi_mem *mem;
> +
> +	mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL);
> +	if (!mem)
> +		return -ENOMEM;
> +
> +	mem->spi = spi;
> +	spi_set_drvdata(spi, mem);
> +
> +	return memdrv->probe(mem);
> +}
> +
> +static int spi_mem_remove(struct spi_device *spi)
> +{
> +	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +	struct spi_mem *mem = spi_get_drvdata(spi);
> +
> +	if (memdrv->remove)
> +		return memdrv->remove(mem);
> +
> +	return 0;
> +}
> +
> +static void spi_mem_shutdown(struct spi_device *spi)
> +{
> +	struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
> +	struct spi_mem *mem = spi_get_drvdata(spi);
> +
> +	if (memdrv->shutdown)
> +		memdrv->shutdown(mem);
> +}
> +
> +/**
> + * spi_mem_driver_register_with_owner() - Register a SPI memory driver
> + * @memdrv: the SPI memory driver to register
> + * @owner: the owner of this driver
> + *
> + * Registers a SPI memory driver.
> + *
> + * Return: 0 in case of success, a negative error core otherwise.
> + */
> +
> +int spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv,
> +				       struct module *owner)
> +{
> +	memdrv->spidrv.probe = spi_mem_probe;
> +	memdrv->spidrv.remove = spi_mem_remove;
> +	memdrv->spidrv.shutdown = spi_mem_shutdown;
> +
> +	return __spi_register_driver(owner, &memdrv->spidrv);
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner);
> +
> +/**
> + * spi_mem_driver_unregister_with_owner() - Unregister a SPI memory driver
> + * @memdrv: the SPI memory driver to unregister
> + *
> + * Unregisters a SPI memory driver.
> + */
> +void spi_mem_driver_unregister(struct spi_mem_driver *memdrv)
> +{
> +	spi_unregister_driver(&memdrv->spidrv);
> +}
> +EXPORT_SYMBOL_GPL(spi_mem_driver_unregister);
> +
>  /*-------------------------------------------------------------------------*/
>  
>  #if IS_ENABLED(CONFIG_OF_DYNAMIC)
> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> index 7b2170bfd6e7..af3c4ac62b55 100644
> --- a/include/linux/spi/spi.h
> +++ b/include/linux/spi/spi.h
> @@ -27,6 +27,7 @@ struct property_entry;
>  struct spi_controller;
>  struct spi_transfer;
>  struct spi_flash_read_message;
> +struct spi_controller_mem_ops;
>  
>  /*
>   * INTERFACES between SPI master-side drivers and SPI slave protocol handlers,
> @@ -376,6 +377,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
>   *                    transfer_one callback.
>   * @handle_err: the subsystem calls the driver to handle an error that occurs
>   *		in the generic implementation of transfer_one_message().
> + * @mem_ops: optimized/dedicated operations for interactions with SPI memory.
> + *	     This field is optional and should only be implemented if the
> + *	     controller has native support for memory like operations.
>   * @unprepare_message: undo any work done by prepare_message().
>   * @slave_abort: abort the ongoing transfer request on an SPI slave controller
>   * @spi_flash_read: to support spi-controller hardwares that provide
> @@ -564,6 +568,9 @@ struct spi_controller {
>  	void (*handle_err)(struct spi_controller *ctlr,
>  			   struct spi_message *message);
>  
> +	/* Optimized handlers for SPI memory-like operations. */
> +	const struct spi_controller_mem_ops *mem_ops;
> +
>  	/* gpio chip select */
>  	int			*cs_gpios;
>  
> @@ -1227,6 +1234,225 @@ int spi_flash_read(struct spi_device *spi,
>  
>  /*---------------------------------------------------------------------------*/
>  
> +/* SPI memory related definitions. */
> +
> +#define SPI_MEM_OP_CMD(__opcode, __buswidth)			\
> +	{							\
> +		.buswidth = __buswidth,				\
> +		.opcode = __opcode,				\
> +	}
> +
> +#define SPI_MEM_OP_ADDRS(__nbytes, __buf, __buswidth)		\
> +	{							\
> +		.nbytes = __nbytes,				\
> +		.buf = __buf,					\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_NO_ADDRS	{ }
> +
> +#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth)			\
> +	{							\
> +		.nbytes = __nbytes,				\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_NO_DUMMY	{ }
> +
> +#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth)		\
> +	{							\
> +		.dir = SPI_MEM_DATA_IN,				\
> +		.nbytes = __nbytes,				\
> +		.buf.in = __buf,				\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth)	\
> +	{							\
> +		.dir = SPI_MEM_DATA_OUT,			\
> +		.nbytes = __nbytes,				\
> +		.buf.out = __buf,				\
> +		.buswidth = __buswidth,				\
> +	}
> +
> +#define SPI_MEM_OP_NO_DATA	{ }
> +
> +/**
> + * enum spi_mem_data_dir - describes the direction of a SPI memory data
> + *			   transfer from the controller perspective
> + * @SPI_MEM_DATA_IN: data coming from the SPI memory
> + * @SPI_MEM_DATA_OUT: data sent the SPI memory
> + */
> +enum spi_mem_data_dir {
> +	SPI_MEM_DATA_IN,
> +	SPI_MEM_DATA_OUT,
> +};
> +
> +/**
> + * struct spi_mem_op - describes a SPI memory operation
> + * @cmd.buswidth: number of IO lines used to transmit the command
> + * @cmd.opcode: operation opcode
> + * @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.buf: buffer storing the address bytes
> + * @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
> + * @data.buswidth: number of IO lanes used to send/receive the data
> + * @data.dir: direction of the transfer
> + * @data.buf.in: input buffer
> + * @data.buf.out: output buffer
> + */
> +struct spi_mem_op {
> +	struct {
> +		u8 buswidth;
> +		u8 opcode;
> +	} cmd;
> +
> +	struct {
> +		u8 nbytes;
> +		u8 buswidth;
> +		const u8 *buf;

For the address value, I would rather use some loff_t type, instead of
const u8 *. 
Actually, most (if not all) SPI flash controllers have some hardware
register to set the address value of the SPI flash command to be
sent. Hence having this value directly in the right format would avoid to
convert. AFAIK, only m25p80 needs to convert from loff_t to u8 * and only
when using the regular SPI API, ie spi_sync(), as the 'struct
spi_flash_read_messag' already uses some integral type too.


> +	} addr;
> +
> +	struct {
> +		u8 nbytes;
> +		u8 buswidth;
> +	} dummy;
> +
> +	struct {
> +		u8 buswidth;
> +		enum spi_mem_data_dir dir;
> +		unsigned int nbytes;
> +		/* buf.{in,out} must be DMA-able. */
> +		union {
> +			void *in;
> +			const void *out;
> +		} buf;
> +	} data;

Also, you should add another member in this structure to set the 'type' of
operation for the SPI flash command:
- register read
- register write
- memory read
- memory write
- memory erase
[- SFDP read ? ]
[- OTP read/write ? ]

Indeed, some SPI flash controller drivers need this information.

For instance the Atmel QSPI controller has some optional feature that allow
to scramble data on the fly from/to the (Q)SPI memory. Hence the scrambling
should be enabled for memory {read,write} (Fast Read, Page Programm)
commands but disabled for register {read,write} commands (Read ID, Write
Enable, Read/Write Status Register, ...).

SPI flash controller drivers *should not* have to check the SPI flash command
instruction op code to guess what kind of operation is being performed.

Another example:
Some (Q)SPI flash controllers map the the SPI flash memory into the system
address space then perform some "memcpy-like" operations to access this SPI
flash memory whereas they rely on reguler register to handle SPI flash
commands reading from or writing into the internal registers fo the SPI
flash. So those controller too need to make the difference between register
and memory operations.

Best regards,

Cyrille

> +};
> +
> +#define SPI_MEM_OP(__cmd, __addr, __dummy, __data)		\
> +	{							\
> +		.cmd = __cmd,					\
> +		.addr = __addr,					\
> +		.dummy = __dummy,				\
> +		.data = __data,					\
> +	}
> +
> +/**
> + * struct spi_mem - describes a SPI memory device
> + * @spi: the underlying SPI device
> + * @drvpriv: spi_mem_drviver private data
> + *
> + * Extra information that describe the SPI memory device and may be needed by
> + * the controller to properly handle this device should be placed here.
> + *
> + * One example would be the device size since some controller expose their SPI
> + * mem devices through a io-mapped region.
> + */
> +struct spi_mem {
> +	struct spi_device *spi;
> +	void *drvpriv;
> +};
> +
> +/**
> + * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
> + *				  device
> + * @mem: memory device
> + * @data: data to attach to the memory device
> + */
> +static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
> +{
> +	mem->drvpriv = data;
> +}
> +
> +/**
> + * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
> + *				  device
> + * @mem: memory device
> + *
> + * Return: the data attached to the mem device.
> + */
> +static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
> +{
> +	return mem->drvpriv;
> +}
> +
> +/**
> + * 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)
> + * @supports_op: check if an operation is supported by the controller
> + * @exec_op: execute a SPI memory operation
> + *
> + * 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.
> + */
> +struct spi_controller_mem_ops {
> +	int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
> +	bool (*supports_op)(struct spi_mem *mem,
> +			    const struct spi_mem_op *op);
> +	int (*exec_op)(struct spi_mem *mem,
> +		       const struct spi_mem_op *op);
> +};
> +
> +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
> +				       const struct spi_mem_op *op,
> +				       struct sg_table *sg);
> +
> +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
> +					  const struct spi_mem_op *op,
> +					  struct sg_table *sg);
> +
> +int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
> +
> +bool spi_mem_supports_op(struct spi_mem *mem,
> +			 const struct spi_mem_op *op);
> +
> +int spi_mem_exec_op(struct spi_mem *mem,
> +		    const struct spi_mem_op *op);
> +
> +/**
> + * struct spi_mem_driver - SPI memory driver
> + * @spidrv: inherit from a SPI driver
> + * @probe: probe a SPI memory. Usually where detection/initialization takes
> + *	   place
> + * @remove: remove a SPI memory
> + * @shutdown: take appropriate action when the system is shutdown
> + *
> + * This is just a thin wrapper around a spi_driver. The core takes care of
> + * allocating the spi_mem object and forwarding the probe/remove/shutdown
> + * request to the spi_mem_driver. The reason we use this wrapper is because
> + * we might have to stuff more information into the spi_mem struct to let
> + * SPI controllers know more about the SPI memory they interact with, and
> + * having this intermediate layer allows us to do that without adding more
> + * useless fields to the spi_device object.
> + */
> +struct spi_mem_driver {
> +	struct spi_driver spidrv;
> +	int (*probe)(struct spi_mem *mem);
> +	int (*remove)(struct spi_mem *mem);
> +	void (*shutdown)(struct spi_mem *mem);
> +};
> +
> +int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
> +				       struct module *owner);
> +
> +#define spi_mem_driver_register(__drv)                                  \
> +	spi_mem_driver_register_with_owner(__drv, THIS_MODULE)
> +
> +void spi_mem_driver_unregister(struct spi_mem_driver *drv);
> +
> +#define module_spi_mem_driver(__drv)                                    \
> +	module_driver(__drv, spi_mem_driver_register,                   \
> +		      spi_mem_driver_unregister)
> +
> +/*---------------------------------------------------------------------------*/
> +
>  /*
>   * INTERFACE between board init code and SPI infrastructure.
>   *
> 

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

* Re: [RFC PATCH 0/6] spi: Extend the framework to generically support memory devices
  2018-02-19 16:25 ` [RFC PATCH 0/6] spi: Extend the framework to generically support memory devices Mark Brown
@ 2018-03-04 21:40     ` Cyrille Pitchen
  2018-03-04 21:40     ` Cyrille Pitchen
  1 sibling, 0 replies; 92+ messages in thread
From: Cyrille Pitchen @ 2018-03-04 21:40 UTC (permalink / raw)
  To: Mark Brown, Boris Brezillon
  Cc: Boris Brezillon, Yogesh Gaur, Vignesh R, Kamal Dasu,
	Richard Weinberger, linux-spi, Peter Pan, Marek Vasut,
	Frieder Schrempf, linux-mtd, Rafał Miłecki,
	Sourav Poddar, Brian Norris, David Woodhouse

Hi Mark,

Le 19/02/2018 à 17:25, Mark Brown a écrit :
> On Tue, Feb 06, 2018 at 12:21:14AM +0100, Boris Brezillon wrote:
> 
>> SPI NAND layer): you can register a SPI NOR device directly from the
>> dedicated SPI memory controller, or it can be registered through the
>> SPI layer if the SPI controller is a generic SPI controller. While
>> the generic SPI controller path works fine, the dedicated SPI NOR
>> controller path brings its own set of issues:
> 
>> * the SPI bus is not represented in sysfs
> 
> I'm not sure if this is a big deal or not - at some point it's just an
> implementation detail of the hardware rather than something we're aware
> of or interested in.
> 
>> * because there's no bus, there's no uevent, which means you'll have to
>>   select both the SPI NAND and SPI NOR logic as soon as one driver
>>   supports both interfaces if you don't want to run into loading
>>   dependency issues
> 
> This is sounding like we want a class (well, virtual bus in the new
> world) for these devices with a SPI based driver sitting on top of that
> for use with genuine SPI controllers.  If the intention is as the
> comments in the code suggested that controllers implementing the memory
> mapping stuff don't use SPI at all then we could have the legacy SPI bus
> support be just another driver for this class.  However when I look at
> what the drivers are actually doing it seems like that's not the case
> and the new API is intended to sit alongside normal SPI support, perhaps
> only implementing certain operations and using regular SPI for others.
> In that case it makes a lot more sense to have this be bolted on the
> side of SPI.
> 

Indeed, if we take the example of the Atmel QSPI controller, the hardware
mode supports two modes:
- the legacy SPI mode: almost the same API (register layout) as the SPI
controller driver in the SPI sub-system (drivers/spi/spi-atmel.c)
- the SPI flash mode: using a new set of registers dedicated to SPI flash
command support and used by the spi-nor driver
(drivers/mtd/spi-nor/atmel_quadspi.c).

With the sama5d2 version for the QSPI controller, in SPI flash mode, all
SPI flash commands - not only Fast Read but also Page Program, register
read/write, ... - must be sent through the AHB memory mapping.
That's why the spi_flash_read() API proposed by the SPI sub-system was not
suited to the Atmel QSPI controller: this current API only supports (Fast)
Read operations but nothing else.

With the API proposed by Boris, the Atmel QSPI driver could be moved into
the SPI sub-system and the controller could also be used if connected to
something else but a SPI flash.

This is only an example but I guess other SPI controllers could be connected
at the same time to both flash and non-flash SPI devices and handle them as
needed from the SPI sub-system, using some dedicated API more suited than
the legacy one when dealing with SPI flash devices.

We already know that other SPI controllers may want to use the "SPI flash"
API only for Fast Read but still rely on the legacy SPI API for all other
SPI flash commands.

So, as you've noticed, the two APIs are not exclusive but should live together.
Then implementing a single SPI controller driver, supporting both APIs, would
really help dealing with concurrency issues between SPI device drivers.

Best regards,

Cyrille

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [RFC PATCH 0/6] spi: Extend the framework to generically support memory devices
@ 2018-03-04 21:40     ` Cyrille Pitchen
  0 siblings, 0 replies; 92+ messages in thread
From: Cyrille Pitchen @ 2018-03-04 21:40 UTC (permalink / raw)
  To: Mark Brown, Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, linux-mtd, linux-spi, Peter Pan,
	Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

Hi Mark,

Le 19/02/2018 à 17:25, Mark Brown a écrit :
> On Tue, Feb 06, 2018 at 12:21:14AM +0100, Boris Brezillon wrote:
> 
>> SPI NAND layer): you can register a SPI NOR device directly from the
>> dedicated SPI memory controller, or it can be registered through the
>> SPI layer if the SPI controller is a generic SPI controller. While
>> the generic SPI controller path works fine, the dedicated SPI NOR
>> controller path brings its own set of issues:
> 
>> * the SPI bus is not represented in sysfs
> 
> I'm not sure if this is a big deal or not - at some point it's just an
> implementation detail of the hardware rather than something we're aware
> of or interested in.
> 
>> * because there's no bus, there's no uevent, which means you'll have to
>>   select both the SPI NAND and SPI NOR logic as soon as one driver
>>   supports both interfaces if you don't want to run into loading
>>   dependency issues
> 
> This is sounding like we want a class (well, virtual bus in the new
> world) for these devices with a SPI based driver sitting on top of that
> for use with genuine SPI controllers.  If the intention is as the
> comments in the code suggested that controllers implementing the memory
> mapping stuff don't use SPI at all then we could have the legacy SPI bus
> support be just another driver for this class.  However when I look at
> what the drivers are actually doing it seems like that's not the case
> and the new API is intended to sit alongside normal SPI support, perhaps
> only implementing certain operations and using regular SPI for others.
> In that case it makes a lot more sense to have this be bolted on the
> side of SPI.
> 

Indeed, if we take the example of the Atmel QSPI controller, the hardware
mode supports two modes:
- the legacy SPI mode: almost the same API (register layout) as the SPI
controller driver in the SPI sub-system (drivers/spi/spi-atmel.c)
- the SPI flash mode: using a new set of registers dedicated to SPI flash
command support and used by the spi-nor driver
(drivers/mtd/spi-nor/atmel_quadspi.c).

With the sama5d2 version for the QSPI controller, in SPI flash mode, all
SPI flash commands - not only Fast Read but also Page Program, register
read/write, ... - must be sent through the AHB memory mapping.
That's why the spi_flash_read() API proposed by the SPI sub-system was not
suited to the Atmel QSPI controller: this current API only supports (Fast)
Read operations but nothing else.

With the API proposed by Boris, the Atmel QSPI driver could be moved into
the SPI sub-system and the controller could also be used if connected to
something else but a SPI flash.

This is only an example but I guess other SPI controllers could be connected
at the same time to both flash and non-flash SPI devices and handle them as
needed from the SPI sub-system, using some dedicated API more suited than
the legacy one when dealing with SPI flash devices.

We already know that other SPI controllers may want to use the "SPI flash"
API only for Fast Read but still rely on the legacy SPI API for all other
SPI flash commands.

So, as you've noticed, the two APIs are not exclusive but should live together.
Then implementing a single SPI controller driver, supporting both APIs, would
really help dealing with concurrency issues between SPI device drivers.

Best regards,

Cyrille

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-03-04 21:15       ` Cyrille Pitchen
@ 2018-03-05  9:00         ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-03-05  9:00 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: Boris Brezillon, Yogesh Gaur, Vignesh R, Kamal Dasu,
	Richard Weinberger, linux-spi, Peter Pan, Marek Vasut,
	Frieder Schrempf, Mark Brown, linux-mtd, Rafał Miłecki,
	Sourav Poddar, Brian Norris, David Woodhouse

Hello Cyrille,

On Sun, 4 Mar 2018 22:15:20 +0100
Cyrille Pitchen <cyrille.pitchen@wedev4u.fr> wrote:

> > +
> > +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
> > +{
> > +	u32 mode = mem->spi->mode;
> > +
> > +	switch (buswidth) {
> > +	case 1:
> > +		return 0;
> > +
> > +	case 2:
> > +		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> > +		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))  
> 
> Some SPI (flash) controller may support Quad protocols but no Duals
> protocols at all. Hence you should only test SPI_{R,T]X_DUAL.

Hm, I'd rather not change that, since it's what have been used so far.
Note that a controller that wants to put more constraints can still
implement the ->supports_op() hook.

> 
> Anyway, I think the function should completely be removed because
> SPI_{R,T}X_{DUAL,QUAD} flags should be deprecated.
> 
> They were fine when introduced long time ago when Quad SPI flashes only
> supported Fast Read 1-1-4 and maybe few of them Page Program 1-1-4.
> 
> However for many years now, Quad SPI flashes support far much SPI protocols
> and commands. For instance:
> Fast Read 1-1-4
> Fast Read 1-4-4
> (Fast Read 4-4-4)
> Page Program 1-1-4
> Page Program 1-4-4
> Page Program 4-4-4
> 
> Fast Read x-y-z means that:
> - the op code is transfered (tx) on x I/O line(s)
> - the address, mode and dummies are transfered (tx) on y I/O line(s)
> - the data are received (rx) on z I/O line(s)
> 
> Page Program x-y-z stands for:
> - the op code being transfered (tx) on x I/O line(s)
> - the address is transfered (tx) on y I/O line(s)
> the data are transfered (tx) on z I/O line(s)
> 
> Then some SPI flash controllers and/or memories may support Fast Read 1-4-4
> but not Page Program 1-1-4 whereas others may support Page Program 1-1-4
> but not Fast Read 1-4-4.
> 
> So using only SPI_{R,T}X_{DUAL,QUAD} flags, how do we make the difference
> between support of Fast Read 1-4-4 and Page Program 1-1-4 or between
> Fast Read 1-1-4 and Fast Read 1-4-4 ?

I think you're mixing 2 different things:

1/ The RX/TX buswidth capabilities for generic SPI/DualSPI/QuadSPI
   transfers, which is, I guess, what these flags have been created for

2/ The buswidth of each instruction of a memory-like operation (by
   instruction I mean, CMD, ADDR, DATA cycles)

There is no reason for a generic SPI controller to specify things for
#2, because it doesn't know anything about this spi-mem protocol, all
it can do is send/recv data using a specific bus width.

> Indeed some latest (Cypress) memories only support Fast Read 1-4-4 but no
> longer Fast Read 1-1-4.

And here you're going to specific. This framework is not here to fully
handle SPI NOR devices, it's here to abstract things enough to make SPI
controllers compatible with various SPI memories or devices that want
to use a similar protocol (FPGAs?).

> 
> I guess we'd rather use something close to the SNOR_HWCAPS_* macros as
> defined in include/linux/mtd/spi-nor.h

->supports_op() should be enough to check such constraints, I don't see
a need for yet another set of macros.

> 
> SPI_{R,T}X_{DUAL,QUAD} flags are no longer suited to SPI flash memories for
> a long time now.

See my explanation above. I don't think SPI_{R,T}X_{DUAL,QUAD} have
been created to handle SPI memories, it's just a way to specify the
supported buswidth for a SPI transfer, that's all. And we still need
them for regular SPI transfers.

> 
> 
> > +			return 0;
> > +
> > +		break;
> > +
> > +	case 4:
> > +		if ((tx && (mode & SPI_TX_QUAD)) ||
> > +		    (!tx && (mode & SPI_RX_QUAD)))
> > +			return 0;
> > +
> > +		break;
> > +
> > +	default:
> > +		break;
> > +	}
> > +
> > +	return -ENOTSUPP;
> > +}
> > +
> > +/**
> > + * spi_mem_supports_op() - Check if a memory device and the controller it is
> > + *			   connected to support a specific memory operation
> > + * @mem: the SPI memory
> > + * @op: the memory operation to check
> > + *
> > + * Some controllers are only supporting Single or Dual IOs, others might only
> > + * support specific opcodes, or it can even be that the controller and device
> > + * both support Quad IOs but the hardware prevents you from using it because
> > + * only 2 IO lines are connected.
> > + *
> > + * This function checks whether a specific operation is supported.
> > + *
> > + * Return: true if @op is supported, false otherwise.
> > + */
> > +bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
> > +{
> > +	struct spi_controller *ctlr = mem->spi->controller;
> > +
> > +	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
> > +		return false;
> > +
> > +	if (op->addr.nbytes &&
> > +	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
> > +		return false;
> > +
> > +	if (op->dummy.nbytes &&
> > +	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
> > +		return false;
> > +
> > +	if (op->data.nbytes &&
> > +	    spi_check_buswidth_req(mem, op->data.buswidth,
> > +				   op->data.dir == SPI_MEM_DATA_IN ?
> > +				   false : true))
> > +		return false;  
> 
> You should remove the 4 tests and calls of spi_check_buswidth_req() above.

Why?

> Based on spi_controller_check_ops(), when ctrl->mem_ops is not NULL,
> mem_ops->supports_op() must be provided, this hook is mandatory.

Well, this was a mistake on my side, see my reply to Vignesh.

> 
> So call this latest hook only, has done just below. You may want to keep the 4
> tests above and spi_check_buswidth_req() but then move them in a
> stand-alone function that could be used as a default implementation of
> mem_ops->supports_op() if you want to allow some SPI controller drivers not
> to implement ->supports_op() by their own.

Okay, that's an option: make ->supports_op() mandatory and then provide
an helper if the controller does not have any extra constraints. I see
one problem with this approach:

> 
> However it should be considered a transitionnal and at some point I think
> SPI_{R,T}X_{DUAL,QUAD} flags should just be removed because many
> developers submitting patches to spi-nor have often been confused by how
> properly use those flags in their drivers.

And here I disagree, again. SPI_{R,T}X_{DUAL,QUAD} are still useful for
regular SPI transfers. I guess not every Dual/Quad SPI device is using
the spi-mem protocol, so we need to support this case too.

> 
> Till then, if you removed the 4 tests above, at least new SPI flash
> controller drivers won't have to set some odd combination of flags in
> spi->mode to make their support of SPI flash memories work as they want.

Hm, I'm not sure about spi->mode. AFAICT, SPI_{R,T}X_{DUAL,QUAD}
flags are only encoding what the device supports, not the current mode.

But I guess you were talking about controller->mode_bits which encodes
the controller capabilities, and I think this one is still useful if
the controller is a generic SPI controller.

As a side note, I'll add that it's possible to have a device supporting
SPI+DualSPI+QuadSPI and still have this device partially connected to
the host controller (some pins left unconnected), hence the
spi-{tx,rx}-bus-width properties in DTs. So we really want to check
the device capabilities, the controller capabilities and the limitation
implied by the board design.

> > +
> > +/**
> > + * struct spi_mem_op - describes a SPI memory operation
> > + * @cmd.buswidth: number of IO lines used to transmit the command
> > + * @cmd.opcode: operation opcode
> > + * @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.buf: buffer storing the address bytes
> > + * @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
> > + * @data.buswidth: number of IO lanes used to send/receive the data
> > + * @data.dir: direction of the transfer
> > + * @data.buf.in: input buffer
> > + * @data.buf.out: output buffer
> > + */
> > +struct spi_mem_op {
> > +	struct {
> > +		u8 buswidth;
> > +		u8 opcode;
> > +	} cmd;
> > +
> > +	struct {
> > +		u8 nbytes;
> > +		u8 buswidth;
> > +		const u8 *buf;  
> 
> For the address value, I would rather use some loff_t type, instead of
> const u8 *. 
> Actually, most (if not all) SPI flash controllers have some hardware
> register to set the address value of the SPI flash command to be
> sent. Hence having this value directly in the right format would avoid to
> convert.

What is the appropriate format? An address encoded in a 64-bit integer
does not give any information on how these bytes should be transmitted
on the bus. SPI NOR is passing the address in big endian, SPI NAND seems
to do the same, but we're not sure this will be the case for all kind of
memories or devices using this spi-mem protocol.

Also, by passing it through an integer we limit ourselves to 8 bytes.
That should be enough, but who knows :-).

If we go for this loff_t field, we'll have to add an endianness field
here and force all drivers to check it.

> AFAIK, only m25p80 needs to convert from loff_t to u8 * and only
> when using the regular SPI API, ie spi_sync(), as the 'struct
> spi_flash_read_messag' already uses some integral type too.

And I'm not sure this was a good idea.

> 
> 
> > +	} addr;
> > +
> > +	struct {
> > +		u8 nbytes;
> > +		u8 buswidth;
> > +	} dummy;
> > +
> > +	struct {
> > +		u8 buswidth;
> > +		enum spi_mem_data_dir dir;
> > +		unsigned int nbytes;
> > +		/* buf.{in,out} must be DMA-able. */
> > +		union {
> > +			void *in;
> > +			const void *out;
> > +		} buf;
> > +	} data;  
> 
> Also, you should add another member in this structure to set the 'type' of
> operation for the SPI flash command:
> - register read
> - register write
> - memory read
> - memory write
> - memory erase
> [- SFDP read ? ]
> [- OTP read/write ? ]

No, really, that's not belonging here. It's entirely SPI NOR specific.

> 
> Indeed, some SPI flash controller drivers need this information.

Then they'll have to be converted/reworked to not depend on that. I did
it for the FSL QSPI controller, and it worked fine. I'm pretty sure
almost all controllers will fit well in the new framework. For those
controllers that really are only able to handle SPI NOR devices (I'm
thinking about the Intel one, but there may be others), then they should
not be converted to the spi_mem approach.

> 
> For instance the Atmel QSPI controller has some optional feature that allow
> to scramble data on the fly from/to the (Q)SPI memory. Hence the scrambling
> should be enabled for memory {read,write} (Fast Read, Page Programm)
> commands but disabled for register {read,write} commands (Read ID, Write
> Enable, Read/Write Status Register, ...).

If scrambling is really something that we need to support, then I think
adding a spi_mem_op->data.scramble boolean would be more appropriate.

> 
> SPI flash controller drivers *should not* have to check the SPI flash command
> instruction op code to guess what kind of operation is being performed.

I completely agree. Not only they shouldn't check the opcode to guess
what is being done, but, IMHO, they should also not try to determine
what kind of operation is being done, so your suggestion to add an
operation type information is not a good idea.

> 
> Another example:
> Some (Q)SPI flash controllers map the the SPI flash memory into the system
> address space then perform some "memcpy-like" operations to access this SPI
> flash memory whereas they rely on reguler register to handle SPI flash
> commands reading from or writing into the internal registers fo the SPI
> flash. So those controller too need to make the difference between register
> and memory operations.

I already thought about the direct mapping stuff, and I think this
should be supported through new hooks in spi_mem_ops (->map() +
->unmap()?).

Thanks for your review.

Boris

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-03-05  9:00         ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-03-05  9:00 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, linux-mtd, Mark Brown, linux-spi, Peter Pan,
	Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

Hello Cyrille,

On Sun, 4 Mar 2018 22:15:20 +0100
Cyrille Pitchen <cyrille.pitchen@wedev4u.fr> wrote:

> > +
> > +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
> > +{
> > +	u32 mode = mem->spi->mode;
> > +
> > +	switch (buswidth) {
> > +	case 1:
> > +		return 0;
> > +
> > +	case 2:
> > +		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> > +		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))  
> 
> Some SPI (flash) controller may support Quad protocols but no Duals
> protocols at all. Hence you should only test SPI_{R,T]X_DUAL.

Hm, I'd rather not change that, since it's what have been used so far.
Note that a controller that wants to put more constraints can still
implement the ->supports_op() hook.

> 
> Anyway, I think the function should completely be removed because
> SPI_{R,T}X_{DUAL,QUAD} flags should be deprecated.
> 
> They were fine when introduced long time ago when Quad SPI flashes only
> supported Fast Read 1-1-4 and maybe few of them Page Program 1-1-4.
> 
> However for many years now, Quad SPI flashes support far much SPI protocols
> and commands. For instance:
> Fast Read 1-1-4
> Fast Read 1-4-4
> (Fast Read 4-4-4)
> Page Program 1-1-4
> Page Program 1-4-4
> Page Program 4-4-4
> 
> Fast Read x-y-z means that:
> - the op code is transfered (tx) on x I/O line(s)
> - the address, mode and dummies are transfered (tx) on y I/O line(s)
> - the data are received (rx) on z I/O line(s)
> 
> Page Program x-y-z stands for:
> - the op code being transfered (tx) on x I/O line(s)
> - the address is transfered (tx) on y I/O line(s)
> the data are transfered (tx) on z I/O line(s)
> 
> Then some SPI flash controllers and/or memories may support Fast Read 1-4-4
> but not Page Program 1-1-4 whereas others may support Page Program 1-1-4
> but not Fast Read 1-4-4.
> 
> So using only SPI_{R,T}X_{DUAL,QUAD} flags, how do we make the difference
> between support of Fast Read 1-4-4 and Page Program 1-1-4 or between
> Fast Read 1-1-4 and Fast Read 1-4-4 ?

I think you're mixing 2 different things:

1/ The RX/TX buswidth capabilities for generic SPI/DualSPI/QuadSPI
   transfers, which is, I guess, what these flags have been created for

2/ The buswidth of each instruction of a memory-like operation (by
   instruction I mean, CMD, ADDR, DATA cycles)

There is no reason for a generic SPI controller to specify things for
#2, because it doesn't know anything about this spi-mem protocol, all
it can do is send/recv data using a specific bus width.

> Indeed some latest (Cypress) memories only support Fast Read 1-4-4 but no
> longer Fast Read 1-1-4.

And here you're going to specific. This framework is not here to fully
handle SPI NOR devices, it's here to abstract things enough to make SPI
controllers compatible with various SPI memories or devices that want
to use a similar protocol (FPGAs?).

> 
> I guess we'd rather use something close to the SNOR_HWCAPS_* macros as
> defined in include/linux/mtd/spi-nor.h

->supports_op() should be enough to check such constraints, I don't see
a need for yet another set of macros.

> 
> SPI_{R,T}X_{DUAL,QUAD} flags are no longer suited to SPI flash memories for
> a long time now.

See my explanation above. I don't think SPI_{R,T}X_{DUAL,QUAD} have
been created to handle SPI memories, it's just a way to specify the
supported buswidth for a SPI transfer, that's all. And we still need
them for regular SPI transfers.

> 
> 
> > +			return 0;
> > +
> > +		break;
> > +
> > +	case 4:
> > +		if ((tx && (mode & SPI_TX_QUAD)) ||
> > +		    (!tx && (mode & SPI_RX_QUAD)))
> > +			return 0;
> > +
> > +		break;
> > +
> > +	default:
> > +		break;
> > +	}
> > +
> > +	return -ENOTSUPP;
> > +}
> > +
> > +/**
> > + * spi_mem_supports_op() - Check if a memory device and the controller it is
> > + *			   connected to support a specific memory operation
> > + * @mem: the SPI memory
> > + * @op: the memory operation to check
> > + *
> > + * Some controllers are only supporting Single or Dual IOs, others might only
> > + * support specific opcodes, or it can even be that the controller and device
> > + * both support Quad IOs but the hardware prevents you from using it because
> > + * only 2 IO lines are connected.
> > + *
> > + * This function checks whether a specific operation is supported.
> > + *
> > + * Return: true if @op is supported, false otherwise.
> > + */
> > +bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
> > +{
> > +	struct spi_controller *ctlr = mem->spi->controller;
> > +
> > +	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
> > +		return false;
> > +
> > +	if (op->addr.nbytes &&
> > +	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
> > +		return false;
> > +
> > +	if (op->dummy.nbytes &&
> > +	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
> > +		return false;
> > +
> > +	if (op->data.nbytes &&
> > +	    spi_check_buswidth_req(mem, op->data.buswidth,
> > +				   op->data.dir == SPI_MEM_DATA_IN ?
> > +				   false : true))
> > +		return false;  
> 
> You should remove the 4 tests and calls of spi_check_buswidth_req() above.

Why?

> Based on spi_controller_check_ops(), when ctrl->mem_ops is not NULL,
> mem_ops->supports_op() must be provided, this hook is mandatory.

Well, this was a mistake on my side, see my reply to Vignesh.

> 
> So call this latest hook only, has done just below. You may want to keep the 4
> tests above and spi_check_buswidth_req() but then move them in a
> stand-alone function that could be used as a default implementation of
> mem_ops->supports_op() if you want to allow some SPI controller drivers not
> to implement ->supports_op() by their own.

Okay, that's an option: make ->supports_op() mandatory and then provide
an helper if the controller does not have any extra constraints. I see
one problem with this approach:

> 
> However it should be considered a transitionnal and at some point I think
> SPI_{R,T}X_{DUAL,QUAD} flags should just be removed because many
> developers submitting patches to spi-nor have often been confused by how
> properly use those flags in their drivers.

And here I disagree, again. SPI_{R,T}X_{DUAL,QUAD} are still useful for
regular SPI transfers. I guess not every Dual/Quad SPI device is using
the spi-mem protocol, so we need to support this case too.

> 
> Till then, if you removed the 4 tests above, at least new SPI flash
> controller drivers won't have to set some odd combination of flags in
> spi->mode to make their support of SPI flash memories work as they want.

Hm, I'm not sure about spi->mode. AFAICT, SPI_{R,T}X_{DUAL,QUAD}
flags are only encoding what the device supports, not the current mode.

But I guess you were talking about controller->mode_bits which encodes
the controller capabilities, and I think this one is still useful if
the controller is a generic SPI controller.

As a side note, I'll add that it's possible to have a device supporting
SPI+DualSPI+QuadSPI and still have this device partially connected to
the host controller (some pins left unconnected), hence the
spi-{tx,rx}-bus-width properties in DTs. So we really want to check
the device capabilities, the controller capabilities and the limitation
implied by the board design.

> > +
> > +/**
> > + * struct spi_mem_op - describes a SPI memory operation
> > + * @cmd.buswidth: number of IO lines used to transmit the command
> > + * @cmd.opcode: operation opcode
> > + * @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.buf: buffer storing the address bytes
> > + * @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
> > + * @data.buswidth: number of IO lanes used to send/receive the data
> > + * @data.dir: direction of the transfer
> > + * @data.buf.in: input buffer
> > + * @data.buf.out: output buffer
> > + */
> > +struct spi_mem_op {
> > +	struct {
> > +		u8 buswidth;
> > +		u8 opcode;
> > +	} cmd;
> > +
> > +	struct {
> > +		u8 nbytes;
> > +		u8 buswidth;
> > +		const u8 *buf;  
> 
> For the address value, I would rather use some loff_t type, instead of
> const u8 *. 
> Actually, most (if not all) SPI flash controllers have some hardware
> register to set the address value of the SPI flash command to be
> sent. Hence having this value directly in the right format would avoid to
> convert.

What is the appropriate format? An address encoded in a 64-bit integer
does not give any information on how these bytes should be transmitted
on the bus. SPI NOR is passing the address in big endian, SPI NAND seems
to do the same, but we're not sure this will be the case for all kind of
memories or devices using this spi-mem protocol.

Also, by passing it through an integer we limit ourselves to 8 bytes.
That should be enough, but who knows :-).

If we go for this loff_t field, we'll have to add an endianness field
here and force all drivers to check it.

> AFAIK, only m25p80 needs to convert from loff_t to u8 * and only
> when using the regular SPI API, ie spi_sync(), as the 'struct
> spi_flash_read_messag' already uses some integral type too.

And I'm not sure this was a good idea.

> 
> 
> > +	} addr;
> > +
> > +	struct {
> > +		u8 nbytes;
> > +		u8 buswidth;
> > +	} dummy;
> > +
> > +	struct {
> > +		u8 buswidth;
> > +		enum spi_mem_data_dir dir;
> > +		unsigned int nbytes;
> > +		/* buf.{in,out} must be DMA-able. */
> > +		union {
> > +			void *in;
> > +			const void *out;
> > +		} buf;
> > +	} data;  
> 
> Also, you should add another member in this structure to set the 'type' of
> operation for the SPI flash command:
> - register read
> - register write
> - memory read
> - memory write
> - memory erase
> [- SFDP read ? ]
> [- OTP read/write ? ]

No, really, that's not belonging here. It's entirely SPI NOR specific.

> 
> Indeed, some SPI flash controller drivers need this information.

Then they'll have to be converted/reworked to not depend on that. I did
it for the FSL QSPI controller, and it worked fine. I'm pretty sure
almost all controllers will fit well in the new framework. For those
controllers that really are only able to handle SPI NOR devices (I'm
thinking about the Intel one, but there may be others), then they should
not be converted to the spi_mem approach.

> 
> For instance the Atmel QSPI controller has some optional feature that allow
> to scramble data on the fly from/to the (Q)SPI memory. Hence the scrambling
> should be enabled for memory {read,write} (Fast Read, Page Programm)
> commands but disabled for register {read,write} commands (Read ID, Write
> Enable, Read/Write Status Register, ...).

If scrambling is really something that we need to support, then I think
adding a spi_mem_op->data.scramble boolean would be more appropriate.

> 
> SPI flash controller drivers *should not* have to check the SPI flash command
> instruction op code to guess what kind of operation is being performed.

I completely agree. Not only they shouldn't check the opcode to guess
what is being done, but, IMHO, they should also not try to determine
what kind of operation is being done, so your suggestion to add an
operation type information is not a good idea.

> 
> Another example:
> Some (Q)SPI flash controllers map the the SPI flash memory into the system
> address space then perform some "memcpy-like" operations to access this SPI
> flash memory whereas they rely on reguler register to handle SPI flash
> commands reading from or writing into the internal registers fo the SPI
> flash. So those controller too need to make the difference between register
> and memory operations.

I already thought about the direct mapping stuff, and I think this
should be supported through new hooks in spi_mem_ops (->map() +
->unmap()?).

Thanks for your review.

Boris

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-03-05  9:00         ` Boris Brezillon
@ 2018-03-05 13:01           ` Cyrille Pitchen
  -1 siblings, 0 replies; 92+ messages in thread
From: Cyrille Pitchen @ 2018-03-05 13:01 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Boris Brezillon, Yogesh Gaur, Vignesh R, Kamal Dasu,
	Richard Weinberger, linux-spi, Peter Pan, Marek Vasut,
	Frieder Schrempf, Mark Brown, linux-mtd, Rafał Miłecki,
	Sourav Poddar, Brian Norris, David Woodhouse

Hi Boris,

Le 05/03/2018 à 10:00, Boris Brezillon a écrit :
> Hello Cyrille,
> 
> On Sun, 4 Mar 2018 22:15:20 +0100
> Cyrille Pitchen <cyrille.pitchen@wedev4u.fr> wrote:
> 
>>> +
>>> +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
>>> +{
>>> +	u32 mode = mem->spi->mode;
>>> +
>>> +	switch (buswidth) {
>>> +	case 1:
>>> +		return 0;
>>> +
>>> +	case 2:
>>> +		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
>>> +		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))  
>>
>> Some SPI (flash) controller may support Quad protocols but no Duals
>> protocols at all. Hence you should only test SPI_{R,T]X_DUAL.
> 
> Hm, I'd rather not change that, since it's what have been used so far.
> Note that a controller that wants to put more constraints can still
> implement the ->supports_op() hook.
> 
>>
>> Anyway, I think the function should completely be removed because
>> SPI_{R,T}X_{DUAL,QUAD} flags should be deprecated.
>>
>> They were fine when introduced long time ago when Quad SPI flashes only
>> supported Fast Read 1-1-4 and maybe few of them Page Program 1-1-4.
>>
>> However for many years now, Quad SPI flashes support far much SPI protocols
>> and commands. For instance:
>> Fast Read 1-1-4
>> Fast Read 1-4-4
>> (Fast Read 4-4-4)
>> Page Program 1-1-4
>> Page Program 1-4-4
>> Page Program 4-4-4
>>
>> Fast Read x-y-z means that:
>> - the op code is transfered (tx) on x I/O line(s)
>> - the address, mode and dummies are transfered (tx) on y I/O line(s)
>> - the data are received (rx) on z I/O line(s)
>>
>> Page Program x-y-z stands for:
>> - the op code being transfered (tx) on x I/O line(s)
>> - the address is transfered (tx) on y I/O line(s)
>> the data are transfered (tx) on z I/O line(s)
>>
>> Then some SPI flash controllers and/or memories may support Fast Read 1-4-4
>> but not Page Program 1-1-4 whereas others may support Page Program 1-1-4
>> but not Fast Read 1-4-4.
>>
>> So using only SPI_{R,T}X_{DUAL,QUAD} flags, how do we make the difference
>> between support of Fast Read 1-4-4 and Page Program 1-1-4 or between
>> Fast Read 1-1-4 and Fast Read 1-4-4 ?
> 
> I think you're mixing 2 different things:
> 
> 1/ The RX/TX buswidth capabilities for generic SPI/DualSPI/QuadSPI
>    transfers, which is, I guess, what these flags have been created for
> 
> 2/ The buswidth of each instruction of a memory-like operation (by
>    instruction I mean, CMD, ADDR, DATA cycles)
> 
> There is no reason for a generic SPI controller to specify things for
> #2, because it doesn't know anything about this spi-mem protocol, all
> it can do is send/recv data using a specific bus width.
>

I might be wrong but I think SPI flashes are the only SPI devices using more
than one I/O line to send and receive data.

Anyway, let assume devices other than SPI flash also use many I/O lines
hence make use of the SPI_{R,T}X_{DUAL,QUAD} flags.

It looks like this series is dealing a new SPI API dedicated to SPI memories.
Just my opinion but 1/ and the associated SPI_{R,T}X_{DUAL,QUAD} flags should
be left only for the generic SPI API, - with spi_sync(), 'struct spi_message',
'struct spi_transfer' and so on.

On the other hand, the new SPI flash dedicated API should only focus on 2/
and totally ignore the SPI_{R,T}X_{DUAL,QUAD} flags.

If we take the example of the sama5d2 QSPI controller, we can use more than one
I/O line only when configure in "serial flash memory" mode. However when in "spi"
mode, when can only use regular MOSI and MISO lines.

With spi_mem_supports_op() as proposed below, we would have to set both SPI_RX_QUAD
and SPI_TX_QUAD to allow Fast Read 1-4-4 when in "serial flash memory" mode.
However with other SPI devices, hence when in "spi" mode, the SPI_{R,T}X_QUAD flags
would still be set though the controller cannot support more than a single I/O line.

The SPI_{R,T}_{DUAL,QUAD} flags could be tested in a default implementation of
spi_mem_supports_op() but should not be tested as mandatory flags for all SPI
(flash) controllers.

 
>> Indeed some latest (Cypress) memories only support Fast Read 1-4-4 but no
>> longer Fast Read 1-1-4.
> 
> And here you're going to specific. This framework is not here to fully
> handle SPI NOR devices, it's here to abstract things enough to make SPI
> controllers compatible with various SPI memories or devices that want
> to use a similar protocol (FPGAs?).
> 
>>
>> I guess we'd rather use something close to the SNOR_HWCAPS_* macros as
>> defined in include/linux/mtd/spi-nor.h
> 
> ->supports_op() should be enough to check such constraints, I don't see
> a need for yet another set of macros.
> 
>>
>> SPI_{R,T}X_{DUAL,QUAD} flags are no longer suited to SPI flash memories for
>> a long time now.
> 
> See my explanation above. I don't think SPI_{R,T}X_{DUAL,QUAD} have
> been created to handle SPI memories, it's just a way to specify the
> supported buswidth for a SPI transfer, that's all. And we still need
> them for regular SPI transfers.
> 
>>
>>
>>> +			return 0;
>>> +
>>> +		break;
>>> +
>>> +	case 4:
>>> +		if ((tx && (mode & SPI_TX_QUAD)) ||
>>> +		    (!tx && (mode & SPI_RX_QUAD)))
>>> +			return 0;
>>> +
>>> +		break;
>>> +
>>> +	default:
>>> +		break;
>>> +	}
>>> +
>>> +	return -ENOTSUPP;
>>> +}
>>> +
>>> +/**
>>> + * spi_mem_supports_op() - Check if a memory device and the controller it is
>>> + *			   connected to support a specific memory operation
>>> + * @mem: the SPI memory
>>> + * @op: the memory operation to check
>>> + *
>>> + * Some controllers are only supporting Single or Dual IOs, others might only
>>> + * support specific opcodes, or it can even be that the controller and device
>>> + * both support Quad IOs but the hardware prevents you from using it because
>>> + * only 2 IO lines are connected.
>>> + *
>>> + * This function checks whether a specific operation is supported.
>>> + *
>>> + * Return: true if @op is supported, false otherwise.
>>> + */
>>> +bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
>>> +{
>>> +	struct spi_controller *ctlr = mem->spi->controller;
>>> +
>>> +	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
>>> +		return false;
>>> +
>>> +	if (op->addr.nbytes &&
>>> +	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
>>> +		return false;
>>> +
>>> +	if (op->dummy.nbytes &&
>>> +	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
>>> +		return false;
>>> +
>>> +	if (op->data.nbytes &&
>>> +	    spi_check_buswidth_req(mem, op->data.buswidth,
>>> +				   op->data.dir == SPI_MEM_DATA_IN ?
>>> +				   false : true))
>>> +		return false;  
>>
>> You should remove the 4 tests and calls of spi_check_buswidth_req() above.
> 
> Why?
> 
>> Based on spi_controller_check_ops(), when ctrl->mem_ops is not NULL,
>> mem_ops->supports_op() must be provided, this hook is mandatory.
> 
> Well, this was a mistake on my side, see my reply to Vignesh.
> 
>>
>> So call this latest hook only, has done just below. You may want to keep the 4
>> tests above and spi_check_buswidth_req() but then move them in a
>> stand-alone function that could be used as a default implementation of
>> mem_ops->supports_op() if you want to allow some SPI controller drivers not
>> to implement ->supports_op() by their own.
> 
> Okay, that's an option: make ->supports_op() mandatory and then provide
> an helper if the controller does not have any extra constraints. I see
> one problem with this approach:
> 
>>
>> However it should be considered a transitionnal and at some point I think
>> SPI_{R,T}X_{DUAL,QUAD} flags should just be removed because many
>> developers submitting patches to spi-nor have often been confused by how
>> properly use those flags in their drivers.
> 
> And here I disagree, again. SPI_{R,T}X_{DUAL,QUAD} are still useful for
> regular SPI transfers. I guess not every Dual/Quad SPI device is using
> the spi-mem protocol, so we need to support this case too.
> 
>>
>> Till then, if you removed the 4 tests above, at least new SPI flash
>> controller drivers won't have to set some odd combination of flags in
>> spi->mode to make their support of SPI flash memories work as they want.
> 
> Hm, I'm not sure about spi->mode. AFAICT, SPI_{R,T}X_{DUAL,QUAD}
> flags are only encoding what the device supports, not the current mode.
> 
> But I guess you were talking about controller->mode_bits which encodes
> the controller capabilities, and I think this one is still useful if
> the controller is a generic SPI controller.
> 
> As a side note, I'll add that it's possible to have a device supporting
> SPI+DualSPI+QuadSPI and still have this device partially connected to
> the host controller (some pins left unconnected), hence the
> spi-{tx,rx}-bus-width properties in DTs. So we really want to check
> the device capabilities, the controller capabilities and the limitation
> implied by the board design.
> 
>>> +
>>> +/**
>>> + * struct spi_mem_op - describes a SPI memory operation
>>> + * @cmd.buswidth: number of IO lines used to transmit the command
>>> + * @cmd.opcode: operation opcode
>>> + * @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.buf: buffer storing the address bytes
>>> + * @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
>>> + * @data.buswidth: number of IO lanes used to send/receive the data
>>> + * @data.dir: direction of the transfer
>>> + * @data.buf.in: input buffer
>>> + * @data.buf.out: output buffer
>>> + */
>>> +struct spi_mem_op {
>>> +	struct {
>>> +		u8 buswidth;
>>> +		u8 opcode;
>>> +	} cmd;
>>> +
>>> +	struct {
>>> +		u8 nbytes;
>>> +		u8 buswidth;
>>> +		const u8 *buf;  
>>
>> For the address value, I would rather use some loff_t type, instead of
>> const u8 *. 
>> Actually, most (if not all) SPI flash controllers have some hardware
>> register to set the address value of the SPI flash command to be
>> sent. Hence having this value directly in the right format would avoid to
>> convert.
> 
> What is the appropriate format? An address encoded in a 64-bit integer
> does not give any information on how these bytes should be transmitted
> on the bus. SPI NOR is passing the address in big endian, SPI NAND seems
> to do the same, but we're not sure this will be the case for all kind of
> memories or devices using this spi-mem protocol.
> 
> Also, by passing it through an integer we limit ourselves to 8 bytes.
> That should be enough, but who knows :-).
> 
> If we go for this loff_t field, we'll have to add an endianness field
> here and force all drivers to check it.
>

I don't think we need any endianness field. We already use loff_t only,
without additional endianness paramater, in spi-nor but also in the above
MTD layer (see 'struct mtd_info') and till now it has just worked fine.

Most, if not all, SPI flash controllers have a dedicated field in one of
their registers to write the address value into it. Generally, this register
is written with an integer in the CPU native byte order so no need to convert.

And anyway, if some conversion is ever needed, the SPI flash controller
driver knows it so we could set as a rule that the integer value for the
address is always stored in the native CPU byte order. At least most SPI flash
controller driver won't have to convert from a const u8 * to some unsigned
integral type more suited to their register accesses.
 
>> AFAIK, only m25p80 needs to convert from loff_t to u8 * and only
>> when using the regular SPI API, ie spi_sync(), as the 'struct
>> spi_flash_read_messag' already uses some integral type too.
> 
> And I'm not sure this was a good idea.
> 
>>
>>
>>> +	} addr;
>>> +
>>> +	struct {
>>> +		u8 nbytes;
>>> +		u8 buswidth;
>>> +	} dummy;
>>> +
>>> +	struct {
>>> +		u8 buswidth;
>>> +		enum spi_mem_data_dir dir;
>>> +		unsigned int nbytes;
>>> +		/* buf.{in,out} must be DMA-able. */
>>> +		union {
>>> +			void *in;
>>> +			const void *out;
>>> +		} buf;
>>> +	} data;  
>>
>> Also, you should add another member in this structure to set the 'type' of
>> operation for the SPI flash command:
>> - register read
>> - register write
>> - memory read
>> - memory write
>> - memory erase
>> [- SFDP read ? ]
>> [- OTP read/write ? ]
> 
> No, really, that's not belonging here. It's entirely SPI NOR specific.
> 

I disagree with you on that. Except for SFDP of course, this will also apply to
SPI NAND as well. And this information should be passed down to the SPI (flash)
controller driver, instead of being lost at the SPI NAND/NOR levels, so the SPI
controller driver could take the proper actions (data cache flush/invalidation
for memory operations, enabling/disabling on-the-fly data scrambling, and other
controller specific constraints).

Best regards,

Cyrille

>>
>> Indeed, some SPI flash controller drivers need this information.
> 
> Then they'll have to be converted/reworked to not depend on that. I did
> it for the FSL QSPI controller, and it worked fine. I'm pretty sure
> almost all controllers will fit well in the new framework. For those
> controllers that really are only able to handle SPI NOR devices (I'm
> thinking about the Intel one, but there may be others), then they should
> not be converted to the spi_mem approach.
> 
>>
>> For instance the Atmel QSPI controller has some optional feature that allow
>> to scramble data on the fly from/to the (Q)SPI memory. Hence the scrambling
>> should be enabled for memory {read,write} (Fast Read, Page Programm)
>> commands but disabled for register {read,write} commands (Read ID, Write
>> Enable, Read/Write Status Register, ...).
> 
> If scrambling is really something that we need to support, then I think
> adding a spi_mem_op->data.scramble boolean would be more appropriate.
> 
>>
>> SPI flash controller drivers *should not* have to check the SPI flash command
>> instruction op code to guess what kind of operation is being performed.
> 
> I completely agree. Not only they shouldn't check the opcode to guess
> what is being done, but, IMHO, they should also not try to determine
> what kind of operation is being done, so your suggestion to add an
> operation type information is not a good idea.
> 
>>
>> Another example:
>> Some (Q)SPI flash controllers map the the SPI flash memory into the system
>> address space then perform some "memcpy-like" operations to access this SPI
>> flash memory whereas they rely on reguler register to handle SPI flash
>> commands reading from or writing into the internal registers fo the SPI
>> flash. So those controller too need to make the difference between register
>> and memory operations.
> 
> I already thought about the direct mapping stuff, and I think this
> should be supported through new hooks in spi_mem_ops (->map() +
> ->unmap()?).
> 
> Thanks for your review.
> 
> Boris
> 

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-03-05 13:01           ` Cyrille Pitchen
  0 siblings, 0 replies; 92+ messages in thread
From: Cyrille Pitchen @ 2018-03-05 13:01 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, linux-mtd, Mark Brown, linux-spi, Peter Pan,
	Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

Hi Boris,

Le 05/03/2018 à 10:00, Boris Brezillon a écrit :
> Hello Cyrille,
> 
> On Sun, 4 Mar 2018 22:15:20 +0100
> Cyrille Pitchen <cyrille.pitchen@wedev4u.fr> wrote:
> 
>>> +
>>> +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
>>> +{
>>> +	u32 mode = mem->spi->mode;
>>> +
>>> +	switch (buswidth) {
>>> +	case 1:
>>> +		return 0;
>>> +
>>> +	case 2:
>>> +		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
>>> +		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))  
>>
>> Some SPI (flash) controller may support Quad protocols but no Duals
>> protocols at all. Hence you should only test SPI_{R,T]X_DUAL.
> 
> Hm, I'd rather not change that, since it's what have been used so far.
> Note that a controller that wants to put more constraints can still
> implement the ->supports_op() hook.
> 
>>
>> Anyway, I think the function should completely be removed because
>> SPI_{R,T}X_{DUAL,QUAD} flags should be deprecated.
>>
>> They were fine when introduced long time ago when Quad SPI flashes only
>> supported Fast Read 1-1-4 and maybe few of them Page Program 1-1-4.
>>
>> However for many years now, Quad SPI flashes support far much SPI protocols
>> and commands. For instance:
>> Fast Read 1-1-4
>> Fast Read 1-4-4
>> (Fast Read 4-4-4)
>> Page Program 1-1-4
>> Page Program 1-4-4
>> Page Program 4-4-4
>>
>> Fast Read x-y-z means that:
>> - the op code is transfered (tx) on x I/O line(s)
>> - the address, mode and dummies are transfered (tx) on y I/O line(s)
>> - the data are received (rx) on z I/O line(s)
>>
>> Page Program x-y-z stands for:
>> - the op code being transfered (tx) on x I/O line(s)
>> - the address is transfered (tx) on y I/O line(s)
>> the data are transfered (tx) on z I/O line(s)
>>
>> Then some SPI flash controllers and/or memories may support Fast Read 1-4-4
>> but not Page Program 1-1-4 whereas others may support Page Program 1-1-4
>> but not Fast Read 1-4-4.
>>
>> So using only SPI_{R,T}X_{DUAL,QUAD} flags, how do we make the difference
>> between support of Fast Read 1-4-4 and Page Program 1-1-4 or between
>> Fast Read 1-1-4 and Fast Read 1-4-4 ?
> 
> I think you're mixing 2 different things:
> 
> 1/ The RX/TX buswidth capabilities for generic SPI/DualSPI/QuadSPI
>    transfers, which is, I guess, what these flags have been created for
> 
> 2/ The buswidth of each instruction of a memory-like operation (by
>    instruction I mean, CMD, ADDR, DATA cycles)
> 
> There is no reason for a generic SPI controller to specify things for
> #2, because it doesn't know anything about this spi-mem protocol, all
> it can do is send/recv data using a specific bus width.
>

I might be wrong but I think SPI flashes are the only SPI devices using more
than one I/O line to send and receive data.

Anyway, let assume devices other than SPI flash also use many I/O lines
hence make use of the SPI_{R,T}X_{DUAL,QUAD} flags.

It looks like this series is dealing a new SPI API dedicated to SPI memories.
Just my opinion but 1/ and the associated SPI_{R,T}X_{DUAL,QUAD} flags should
be left only for the generic SPI API, - with spi_sync(), 'struct spi_message',
'struct spi_transfer' and so on.

On the other hand, the new SPI flash dedicated API should only focus on 2/
and totally ignore the SPI_{R,T}X_{DUAL,QUAD} flags.

If we take the example of the sama5d2 QSPI controller, we can use more than one
I/O line only when configure in "serial flash memory" mode. However when in "spi"
mode, when can only use regular MOSI and MISO lines.

With spi_mem_supports_op() as proposed below, we would have to set both SPI_RX_QUAD
and SPI_TX_QUAD to allow Fast Read 1-4-4 when in "serial flash memory" mode.
However with other SPI devices, hence when in "spi" mode, the SPI_{R,T}X_QUAD flags
would still be set though the controller cannot support more than a single I/O line.

The SPI_{R,T}_{DUAL,QUAD} flags could be tested in a default implementation of
spi_mem_supports_op() but should not be tested as mandatory flags for all SPI
(flash) controllers.

 
>> Indeed some latest (Cypress) memories only support Fast Read 1-4-4 but no
>> longer Fast Read 1-1-4.
> 
> And here you're going to specific. This framework is not here to fully
> handle SPI NOR devices, it's here to abstract things enough to make SPI
> controllers compatible with various SPI memories or devices that want
> to use a similar protocol (FPGAs?).
> 
>>
>> I guess we'd rather use something close to the SNOR_HWCAPS_* macros as
>> defined in include/linux/mtd/spi-nor.h
> 
> ->supports_op() should be enough to check such constraints, I don't see
> a need for yet another set of macros.
> 
>>
>> SPI_{R,T}X_{DUAL,QUAD} flags are no longer suited to SPI flash memories for
>> a long time now.
> 
> See my explanation above. I don't think SPI_{R,T}X_{DUAL,QUAD} have
> been created to handle SPI memories, it's just a way to specify the
> supported buswidth for a SPI transfer, that's all. And we still need
> them for regular SPI transfers.
> 
>>
>>
>>> +			return 0;
>>> +
>>> +		break;
>>> +
>>> +	case 4:
>>> +		if ((tx && (mode & SPI_TX_QUAD)) ||
>>> +		    (!tx && (mode & SPI_RX_QUAD)))
>>> +			return 0;
>>> +
>>> +		break;
>>> +
>>> +	default:
>>> +		break;
>>> +	}
>>> +
>>> +	return -ENOTSUPP;
>>> +}
>>> +
>>> +/**
>>> + * spi_mem_supports_op() - Check if a memory device and the controller it is
>>> + *			   connected to support a specific memory operation
>>> + * @mem: the SPI memory
>>> + * @op: the memory operation to check
>>> + *
>>> + * Some controllers are only supporting Single or Dual IOs, others might only
>>> + * support specific opcodes, or it can even be that the controller and device
>>> + * both support Quad IOs but the hardware prevents you from using it because
>>> + * only 2 IO lines are connected.
>>> + *
>>> + * This function checks whether a specific operation is supported.
>>> + *
>>> + * Return: true if @op is supported, false otherwise.
>>> + */
>>> +bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
>>> +{
>>> +	struct spi_controller *ctlr = mem->spi->controller;
>>> +
>>> +	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
>>> +		return false;
>>> +
>>> +	if (op->addr.nbytes &&
>>> +	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
>>> +		return false;
>>> +
>>> +	if (op->dummy.nbytes &&
>>> +	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
>>> +		return false;
>>> +
>>> +	if (op->data.nbytes &&
>>> +	    spi_check_buswidth_req(mem, op->data.buswidth,
>>> +				   op->data.dir == SPI_MEM_DATA_IN ?
>>> +				   false : true))
>>> +		return false;  
>>
>> You should remove the 4 tests and calls of spi_check_buswidth_req() above.
> 
> Why?
> 
>> Based on spi_controller_check_ops(), when ctrl->mem_ops is not NULL,
>> mem_ops->supports_op() must be provided, this hook is mandatory.
> 
> Well, this was a mistake on my side, see my reply to Vignesh.
> 
>>
>> So call this latest hook only, has done just below. You may want to keep the 4
>> tests above and spi_check_buswidth_req() but then move them in a
>> stand-alone function that could be used as a default implementation of
>> mem_ops->supports_op() if you want to allow some SPI controller drivers not
>> to implement ->supports_op() by their own.
> 
> Okay, that's an option: make ->supports_op() mandatory and then provide
> an helper if the controller does not have any extra constraints. I see
> one problem with this approach:
> 
>>
>> However it should be considered a transitionnal and at some point I think
>> SPI_{R,T}X_{DUAL,QUAD} flags should just be removed because many
>> developers submitting patches to spi-nor have often been confused by how
>> properly use those flags in their drivers.
> 
> And here I disagree, again. SPI_{R,T}X_{DUAL,QUAD} are still useful for
> regular SPI transfers. I guess not every Dual/Quad SPI device is using
> the spi-mem protocol, so we need to support this case too.
> 
>>
>> Till then, if you removed the 4 tests above, at least new SPI flash
>> controller drivers won't have to set some odd combination of flags in
>> spi->mode to make their support of SPI flash memories work as they want.
> 
> Hm, I'm not sure about spi->mode. AFAICT, SPI_{R,T}X_{DUAL,QUAD}
> flags are only encoding what the device supports, not the current mode.
> 
> But I guess you were talking about controller->mode_bits which encodes
> the controller capabilities, and I think this one is still useful if
> the controller is a generic SPI controller.
> 
> As a side note, I'll add that it's possible to have a device supporting
> SPI+DualSPI+QuadSPI and still have this device partially connected to
> the host controller (some pins left unconnected), hence the
> spi-{tx,rx}-bus-width properties in DTs. So we really want to check
> the device capabilities, the controller capabilities and the limitation
> implied by the board design.
> 
>>> +
>>> +/**
>>> + * struct spi_mem_op - describes a SPI memory operation
>>> + * @cmd.buswidth: number of IO lines used to transmit the command
>>> + * @cmd.opcode: operation opcode
>>> + * @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.buf: buffer storing the address bytes
>>> + * @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
>>> + * @data.buswidth: number of IO lanes used to send/receive the data
>>> + * @data.dir: direction of the transfer
>>> + * @data.buf.in: input buffer
>>> + * @data.buf.out: output buffer
>>> + */
>>> +struct spi_mem_op {
>>> +	struct {
>>> +		u8 buswidth;
>>> +		u8 opcode;
>>> +	} cmd;
>>> +
>>> +	struct {
>>> +		u8 nbytes;
>>> +		u8 buswidth;
>>> +		const u8 *buf;  
>>
>> For the address value, I would rather use some loff_t type, instead of
>> const u8 *. 
>> Actually, most (if not all) SPI flash controllers have some hardware
>> register to set the address value of the SPI flash command to be
>> sent. Hence having this value directly in the right format would avoid to
>> convert.
> 
> What is the appropriate format? An address encoded in a 64-bit integer
> does not give any information on how these bytes should be transmitted
> on the bus. SPI NOR is passing the address in big endian, SPI NAND seems
> to do the same, but we're not sure this will be the case for all kind of
> memories or devices using this spi-mem protocol.
> 
> Also, by passing it through an integer we limit ourselves to 8 bytes.
> That should be enough, but who knows :-).
> 
> If we go for this loff_t field, we'll have to add an endianness field
> here and force all drivers to check it.
>

I don't think we need any endianness field. We already use loff_t only,
without additional endianness paramater, in spi-nor but also in the above
MTD layer (see 'struct mtd_info') and till now it has just worked fine.

Most, if not all, SPI flash controllers have a dedicated field in one of
their registers to write the address value into it. Generally, this register
is written with an integer in the CPU native byte order so no need to convert.

And anyway, if some conversion is ever needed, the SPI flash controller
driver knows it so we could set as a rule that the integer value for the
address is always stored in the native CPU byte order. At least most SPI flash
controller driver won't have to convert from a const u8 * to some unsigned
integral type more suited to their register accesses.
 
>> AFAIK, only m25p80 needs to convert from loff_t to u8 * and only
>> when using the regular SPI API, ie spi_sync(), as the 'struct
>> spi_flash_read_messag' already uses some integral type too.
> 
> And I'm not sure this was a good idea.
> 
>>
>>
>>> +	} addr;
>>> +
>>> +	struct {
>>> +		u8 nbytes;
>>> +		u8 buswidth;
>>> +	} dummy;
>>> +
>>> +	struct {
>>> +		u8 buswidth;
>>> +		enum spi_mem_data_dir dir;
>>> +		unsigned int nbytes;
>>> +		/* buf.{in,out} must be DMA-able. */
>>> +		union {
>>> +			void *in;
>>> +			const void *out;
>>> +		} buf;
>>> +	} data;  
>>
>> Also, you should add another member in this structure to set the 'type' of
>> operation for the SPI flash command:
>> - register read
>> - register write
>> - memory read
>> - memory write
>> - memory erase
>> [- SFDP read ? ]
>> [- OTP read/write ? ]
> 
> No, really, that's not belonging here. It's entirely SPI NOR specific.
> 

I disagree with you on that. Except for SFDP of course, this will also apply to
SPI NAND as well. And this information should be passed down to the SPI (flash)
controller driver, instead of being lost at the SPI NAND/NOR levels, so the SPI
controller driver could take the proper actions (data cache flush/invalidation
for memory operations, enabling/disabling on-the-fly data scrambling, and other
controller specific constraints).

Best regards,

Cyrille

>>
>> Indeed, some SPI flash controller drivers need this information.
> 
> Then they'll have to be converted/reworked to not depend on that. I did
> it for the FSL QSPI controller, and it worked fine. I'm pretty sure
> almost all controllers will fit well in the new framework. For those
> controllers that really are only able to handle SPI NOR devices (I'm
> thinking about the Intel one, but there may be others), then they should
> not be converted to the spi_mem approach.
> 
>>
>> For instance the Atmel QSPI controller has some optional feature that allow
>> to scramble data on the fly from/to the (Q)SPI memory. Hence the scrambling
>> should be enabled for memory {read,write} (Fast Read, Page Programm)
>> commands but disabled for register {read,write} commands (Read ID, Write
>> Enable, Read/Write Status Register, ...).
> 
> If scrambling is really something that we need to support, then I think
> adding a spi_mem_op->data.scramble boolean would be more appropriate.
> 
>>
>> SPI flash controller drivers *should not* have to check the SPI flash command
>> instruction op code to guess what kind of operation is being performed.
> 
> I completely agree. Not only they shouldn't check the opcode to guess
> what is being done, but, IMHO, they should also not try to determine
> what kind of operation is being done, so your suggestion to add an
> operation type information is not a good idea.
> 
>>
>> Another example:
>> Some (Q)SPI flash controllers map the the SPI flash memory into the system
>> address space then perform some "memcpy-like" operations to access this SPI
>> flash memory whereas they rely on reguler register to handle SPI flash
>> commands reading from or writing into the internal registers fo the SPI
>> flash. So those controller too need to make the difference between register
>> and memory operations.
> 
> I already thought about the direct mapping stuff, and I think this
> should be supported through new hooks in spi_mem_ops (->map() +
> ->unmap()?).
> 
> Thanks for your review.
> 
> Boris
> 

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-03-05 13:01           ` Cyrille Pitchen
@ 2018-03-05 13:47             ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-03-05 13:47 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: Boris Brezillon, Yogesh Gaur, Vignesh R, Kamal Dasu,
	Richard Weinberger, linux-spi, Peter Pan, Marek Vasut,
	Frieder Schrempf, Mark Brown, linux-mtd, Rafał Miłecki,
	Sourav Poddar, Brian Norris, David Woodhouse

On Mon, 5 Mar 2018 14:01:59 +0100
Cyrille Pitchen <cyrille.pitchen@wedev4u.fr> wrote:

> Hi Boris,
> 
> Le 05/03/2018 à 10:00, Boris Brezillon a écrit :
> > Hello Cyrille,
> > 
> > On Sun, 4 Mar 2018 22:15:20 +0100
> > Cyrille Pitchen <cyrille.pitchen@wedev4u.fr> wrote:
> >   
> >>> +
> >>> +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
> >>> +{
> >>> +	u32 mode = mem->spi->mode;
> >>> +
> >>> +	switch (buswidth) {
> >>> +	case 1:
> >>> +		return 0;
> >>> +
> >>> +	case 2:
> >>> +		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> >>> +		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))    
> >>
> >> Some SPI (flash) controller may support Quad protocols but no Duals
> >> protocols at all. Hence you should only test SPI_{R,T]X_DUAL.  
> > 
> > Hm, I'd rather not change that, since it's what have been used so far.
> > Note that a controller that wants to put more constraints can still
> > implement the ->supports_op() hook.
> >   
> >>
> >> Anyway, I think the function should completely be removed because
> >> SPI_{R,T}X_{DUAL,QUAD} flags should be deprecated.
> >>
> >> They were fine when introduced long time ago when Quad SPI flashes only
> >> supported Fast Read 1-1-4 and maybe few of them Page Program 1-1-4.
> >>
> >> However for many years now, Quad SPI flashes support far much SPI protocols
> >> and commands. For instance:
> >> Fast Read 1-1-4
> >> Fast Read 1-4-4
> >> (Fast Read 4-4-4)
> >> Page Program 1-1-4
> >> Page Program 1-4-4
> >> Page Program 4-4-4
> >>
> >> Fast Read x-y-z means that:
> >> - the op code is transfered (tx) on x I/O line(s)
> >> - the address, mode and dummies are transfered (tx) on y I/O line(s)
> >> - the data are received (rx) on z I/O line(s)
> >>
> >> Page Program x-y-z stands for:
> >> - the op code being transfered (tx) on x I/O line(s)
> >> - the address is transfered (tx) on y I/O line(s)
> >> the data are transfered (tx) on z I/O line(s)
> >>
> >> Then some SPI flash controllers and/or memories may support Fast Read 1-4-4
> >> but not Page Program 1-1-4 whereas others may support Page Program 1-1-4
> >> but not Fast Read 1-4-4.
> >>
> >> So using only SPI_{R,T}X_{DUAL,QUAD} flags, how do we make the difference
> >> between support of Fast Read 1-4-4 and Page Program 1-1-4 or between
> >> Fast Read 1-1-4 and Fast Read 1-4-4 ?  
> > 
> > I think you're mixing 2 different things:
> > 
> > 1/ The RX/TX buswidth capabilities for generic SPI/DualSPI/QuadSPI
> >    transfers, which is, I guess, what these flags have been created for
> > 
> > 2/ The buswidth of each instruction of a memory-like operation (by
> >    instruction I mean, CMD, ADDR, DATA cycles)
> > 
> > There is no reason for a generic SPI controller to specify things for
> > #2, because it doesn't know anything about this spi-mem protocol, all
> > it can do is send/recv data using a specific bus width.
> >  
> 
> I might be wrong but I think SPI flashes are the only SPI devices using more
> than one I/O line to send and receive data.

FPGAs, and I there are and will be other devices using the spi-mem
protocol to address non-memory devices. After all, this protocol is
just enforcing a specific sequence that has nothing memory-specific.

> 
> Anyway, let assume devices other than SPI flash also use many I/O lines
> hence make use of the SPI_{R,T}X_{DUAL,QUAD} flags.
> 
> It looks like this series is dealing a new SPI API dedicated to SPI memories.

I said memory-like devices, and it's actually about supporting any kind
of devices that follow the spi-mem protocol, or in other words, any
kind of communication where transactions are of the following form:

1/ an opcode
2/ 0 to N address cycles
3/ 0 to N dummy cycles
4/ 0 to N data in/out cycles

> Just my opinion but 1/ and the associated SPI_{R,T}X_{DUAL,QUAD} flags should
> be left only for the generic SPI API, - with spi_sync(), 'struct spi_message',
> 'struct spi_transfer' and so on.

And that's what pretty much what I'm doing: a controller implementing
the spi_mem_ops interface can check the operation by itself, if the
default check based on SPI_{R,T}X_{DUAL,QUAD} flags is not sufficient.

> 
> On the other hand, the new SPI flash dedicated API should only focus on 2/
> and totally ignore the SPI_{R,T}X_{DUAL,QUAD} flags.

Not when we fallback to the generic API, but otherwise, I'm fine with
making ->supports_op() mandatory and only providing the generic checks
as a helper.

> 
> If we take the example of the sama5d2 QSPI controller, we can use more than one
> I/O line only when configure in "serial flash memory" mode. However when in "spi"
> mode, when can only use regular MOSI and MISO lines.
> 
> With spi_mem_supports_op() as proposed below, we would have to set both SPI_RX_QUAD
> and SPI_TX_QUAD to allow Fast Read 1-4-4 when in "serial flash memory" mode.
> However with other SPI devices, hence when in "spi" mode, the SPI_{R,T}X_QUAD flags
> would still be set though the controller cannot support more than a single I/O line.

Okay, that's a good reason to get this generic check out of the
spi_mem_supports_op() path and instead provide a generic helper that
can be used by drivers which wants to rely on SPI_{R,T}X_{DUAL,QUAD}
flags.

> 
> The SPI_{R,T}_{DUAL,QUAD} flags could be tested in a default implementation of
> spi_mem_supports_op() but should not be tested as mandatory flags for all SPI
> (flash) controllers.

Agreed. I still think we need a way to restrict the number of IO-lines
based on board-related information, and if spi->mode is not appropriate
for that, we need it somewhere else. Otherwise, you might end-up
assuming QSPI is supported by both the device and the controller, while
the connections on the board is, for instance, limiting it to SPI or
DualSPI.


> >   
> >>> +
> >>> +/**
> >>> + * struct spi_mem_op - describes a SPI memory operation
> >>> + * @cmd.buswidth: number of IO lines used to transmit the command
> >>> + * @cmd.opcode: operation opcode
> >>> + * @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.buf: buffer storing the address bytes
> >>> + * @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
> >>> + * @data.buswidth: number of IO lanes used to send/receive the data
> >>> + * @data.dir: direction of the transfer
> >>> + * @data.buf.in: input buffer
> >>> + * @data.buf.out: output buffer
> >>> + */
> >>> +struct spi_mem_op {
> >>> +	struct {
> >>> +		u8 buswidth;
> >>> +		u8 opcode;
> >>> +	} cmd;
> >>> +
> >>> +	struct {
> >>> +		u8 nbytes;
> >>> +		u8 buswidth;
> >>> +		const u8 *buf;    
> >>
> >> For the address value, I would rather use some loff_t type, instead of
> >> const u8 *. 
> >> Actually, most (if not all) SPI flash controllers have some hardware
> >> register to set the address value of the SPI flash command to be
> >> sent. Hence having this value directly in the right format would avoid to
> >> convert.  
> > 
> > What is the appropriate format? An address encoded in a 64-bit integer
> > does not give any information on how these bytes should be transmitted
> > on the bus. SPI NOR is passing the address in big endian, SPI NAND seems
> > to do the same, but we're not sure this will be the case for all kind of
> > memories or devices using this spi-mem protocol.
> > 
> > Also, by passing it through an integer we limit ourselves to 8 bytes.
> > That should be enough, but who knows :-).
> > 
> > If we go for this loff_t field, we'll have to add an endianness field
> > here and force all drivers to check it.
> >  
> 
> I don't think we need any endianness field. We already use loff_t only,
> without additional endianness paramater, in spi-nor but also in the above
> MTD layer (see 'struct mtd_info') and till now it has just worked fine.

It's not about the MTD layer. Can you predict that all devices will
transmit the address in big-endian? I can't.

> 
> Most, if not all, SPI flash controllers have a dedicated field in one of
> their registers to write the address value into it. Generally, this register
> is written with an integer in the CPU native byte order so no need to convert.

It's not about how you write your register, but in which order address
bytes are transmitted on the SPI wire. Currently, SPI NOR use the MSB
first scheme, but that won't necessarily be the case for other SPI
devices.

> 
> And anyway, if some conversion is ever needed, the SPI flash controller
> driver knows it so we could set as a rule that the integer value for the
> address is always stored in the native CPU byte order. At least most SPI flash
> controller driver won't have to convert from a const u8 * to some unsigned
> integral type more suited to their register accesses.

They will have to at least make sure it's properly encoded: if the
device is expecting the address to be transmitted LSB first (AKA
little-endian), they'll have to swap address bytes before writing the
value to the register.

>  
> >> AFAIK, only m25p80 needs to convert from loff_t to u8 * and only
> >> when using the regular SPI API, ie spi_sync(), as the 'struct
> >> spi_flash_read_messag' already uses some integral type too.  
> > 
> > And I'm not sure this was a good idea.
> >   
> >>
> >>  
> >>> +	} addr;
> >>> +
> >>> +	struct {
> >>> +		u8 nbytes;
> >>> +		u8 buswidth;
> >>> +	} dummy;
> >>> +
> >>> +	struct {
> >>> +		u8 buswidth;
> >>> +		enum spi_mem_data_dir dir;
> >>> +		unsigned int nbytes;
> >>> +		/* buf.{in,out} must be DMA-able. */
> >>> +		union {
> >>> +			void *in;
> >>> +			const void *out;
> >>> +		} buf;
> >>> +	} data;    
> >>
> >> Also, you should add another member in this structure to set the 'type' of
> >> operation for the SPI flash command:
> >> - register read
> >> - register write
> >> - memory read
> >> - memory write
> >> - memory erase
> >> [- SFDP read ? ]
> >> [- OTP read/write ? ]  
> > 
> > No, really, that's not belonging here. It's entirely SPI NOR specific.
> >   
> 
> I disagree with you on that. Except for SFDP of course, this will also apply to
> SPI NAND as well. And this information should be passed down to the SPI (flash)
> controller driver, instead of being lost at the SPI NAND/NOR levels,

I'm not saying we should keep these information at the spi-nor/nand
level, just saying we should find a way to make it look like a generic
solution so that SPI controllers don't have to guess what they should
do based on the memory operation type.

Typically, why should a SPI controller care about whether the operation
is an erase, a reg read or a reg write. From the controller PoV, it's
just an spi-mem operation.
The use cases you want to optimize are read/write user-data, and this
can be done in different ways (direct memory mapping is one solution).

> so the SPI
> controller driver could take the proper actions (data cache flush/invalidation
> for memory operations,

This one has to do with direct mapping, so an API to allow direct
mapping should help address that.

> enabling/disabling on-the-fly data scrambling,

And what's the problem with passing the scramble information on a
per-operation basis and letting the upper layer decide when it's a good
idea to use it or not?

> and other
> controller specific constraints).

Could you be more specific?

I really have the feeling you're reviewing that with your SPI NOR
maintainer eye, while, as I said several times, I'm trying to
address the problem more generically. My intent is not to move every
bits you have in the spi-nor layer to the spi layer. Instead, we can
progressively provide a consistent API that helps you deal with memory
devices (I guess supporting direct memory mapping is the next step if
we want to get the best perfs out of these advanced controllers) in a
device agnostic fashion.

Regards,

Boris

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-03-05 13:47             ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-03-05 13:47 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, linux-mtd, Mark Brown, linux-spi, Peter Pan,
	Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

On Mon, 5 Mar 2018 14:01:59 +0100
Cyrille Pitchen <cyrille.pitchen@wedev4u.fr> wrote:

> Hi Boris,
> 
> Le 05/03/2018 à 10:00, Boris Brezillon a écrit :
> > Hello Cyrille,
> > 
> > On Sun, 4 Mar 2018 22:15:20 +0100
> > Cyrille Pitchen <cyrille.pitchen@wedev4u.fr> wrote:
> >   
> >>> +
> >>> +static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
> >>> +{
> >>> +	u32 mode = mem->spi->mode;
> >>> +
> >>> +	switch (buswidth) {
> >>> +	case 1:
> >>> +		return 0;
> >>> +
> >>> +	case 2:
> >>> +		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
> >>> +		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))    
> >>
> >> Some SPI (flash) controller may support Quad protocols but no Duals
> >> protocols at all. Hence you should only test SPI_{R,T]X_DUAL.  
> > 
> > Hm, I'd rather not change that, since it's what have been used so far.
> > Note that a controller that wants to put more constraints can still
> > implement the ->supports_op() hook.
> >   
> >>
> >> Anyway, I think the function should completely be removed because
> >> SPI_{R,T}X_{DUAL,QUAD} flags should be deprecated.
> >>
> >> They were fine when introduced long time ago when Quad SPI flashes only
> >> supported Fast Read 1-1-4 and maybe few of them Page Program 1-1-4.
> >>
> >> However for many years now, Quad SPI flashes support far much SPI protocols
> >> and commands. For instance:
> >> Fast Read 1-1-4
> >> Fast Read 1-4-4
> >> (Fast Read 4-4-4)
> >> Page Program 1-1-4
> >> Page Program 1-4-4
> >> Page Program 4-4-4
> >>
> >> Fast Read x-y-z means that:
> >> - the op code is transfered (tx) on x I/O line(s)
> >> - the address, mode and dummies are transfered (tx) on y I/O line(s)
> >> - the data are received (rx) on z I/O line(s)
> >>
> >> Page Program x-y-z stands for:
> >> - the op code being transfered (tx) on x I/O line(s)
> >> - the address is transfered (tx) on y I/O line(s)
> >> the data are transfered (tx) on z I/O line(s)
> >>
> >> Then some SPI flash controllers and/or memories may support Fast Read 1-4-4
> >> but not Page Program 1-1-4 whereas others may support Page Program 1-1-4
> >> but not Fast Read 1-4-4.
> >>
> >> So using only SPI_{R,T}X_{DUAL,QUAD} flags, how do we make the difference
> >> between support of Fast Read 1-4-4 and Page Program 1-1-4 or between
> >> Fast Read 1-1-4 and Fast Read 1-4-4 ?  
> > 
> > I think you're mixing 2 different things:
> > 
> > 1/ The RX/TX buswidth capabilities for generic SPI/DualSPI/QuadSPI
> >    transfers, which is, I guess, what these flags have been created for
> > 
> > 2/ The buswidth of each instruction of a memory-like operation (by
> >    instruction I mean, CMD, ADDR, DATA cycles)
> > 
> > There is no reason for a generic SPI controller to specify things for
> > #2, because it doesn't know anything about this spi-mem protocol, all
> > it can do is send/recv data using a specific bus width.
> >  
> 
> I might be wrong but I think SPI flashes are the only SPI devices using more
> than one I/O line to send and receive data.

FPGAs, and I there are and will be other devices using the spi-mem
protocol to address non-memory devices. After all, this protocol is
just enforcing a specific sequence that has nothing memory-specific.

> 
> Anyway, let assume devices other than SPI flash also use many I/O lines
> hence make use of the SPI_{R,T}X_{DUAL,QUAD} flags.
> 
> It looks like this series is dealing a new SPI API dedicated to SPI memories.

I said memory-like devices, and it's actually about supporting any kind
of devices that follow the spi-mem protocol, or in other words, any
kind of communication where transactions are of the following form:

1/ an opcode
2/ 0 to N address cycles
3/ 0 to N dummy cycles
4/ 0 to N data in/out cycles

> Just my opinion but 1/ and the associated SPI_{R,T}X_{DUAL,QUAD} flags should
> be left only for the generic SPI API, - with spi_sync(), 'struct spi_message',
> 'struct spi_transfer' and so on.

And that's what pretty much what I'm doing: a controller implementing
the spi_mem_ops interface can check the operation by itself, if the
default check based on SPI_{R,T}X_{DUAL,QUAD} flags is not sufficient.

> 
> On the other hand, the new SPI flash dedicated API should only focus on 2/
> and totally ignore the SPI_{R,T}X_{DUAL,QUAD} flags.

Not when we fallback to the generic API, but otherwise, I'm fine with
making ->supports_op() mandatory and only providing the generic checks
as a helper.

> 
> If we take the example of the sama5d2 QSPI controller, we can use more than one
> I/O line only when configure in "serial flash memory" mode. However when in "spi"
> mode, when can only use regular MOSI and MISO lines.
> 
> With spi_mem_supports_op() as proposed below, we would have to set both SPI_RX_QUAD
> and SPI_TX_QUAD to allow Fast Read 1-4-4 when in "serial flash memory" mode.
> However with other SPI devices, hence when in "spi" mode, the SPI_{R,T}X_QUAD flags
> would still be set though the controller cannot support more than a single I/O line.

Okay, that's a good reason to get this generic check out of the
spi_mem_supports_op() path and instead provide a generic helper that
can be used by drivers which wants to rely on SPI_{R,T}X_{DUAL,QUAD}
flags.

> 
> The SPI_{R,T}_{DUAL,QUAD} flags could be tested in a default implementation of
> spi_mem_supports_op() but should not be tested as mandatory flags for all SPI
> (flash) controllers.

Agreed. I still think we need a way to restrict the number of IO-lines
based on board-related information, and if spi->mode is not appropriate
for that, we need it somewhere else. Otherwise, you might end-up
assuming QSPI is supported by both the device and the controller, while
the connections on the board is, for instance, limiting it to SPI or
DualSPI.


> >   
> >>> +
> >>> +/**
> >>> + * struct spi_mem_op - describes a SPI memory operation
> >>> + * @cmd.buswidth: number of IO lines used to transmit the command
> >>> + * @cmd.opcode: operation opcode
> >>> + * @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.buf: buffer storing the address bytes
> >>> + * @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
> >>> + * @data.buswidth: number of IO lanes used to send/receive the data
> >>> + * @data.dir: direction of the transfer
> >>> + * @data.buf.in: input buffer
> >>> + * @data.buf.out: output buffer
> >>> + */
> >>> +struct spi_mem_op {
> >>> +	struct {
> >>> +		u8 buswidth;
> >>> +		u8 opcode;
> >>> +	} cmd;
> >>> +
> >>> +	struct {
> >>> +		u8 nbytes;
> >>> +		u8 buswidth;
> >>> +		const u8 *buf;    
> >>
> >> For the address value, I would rather use some loff_t type, instead of
> >> const u8 *. 
> >> Actually, most (if not all) SPI flash controllers have some hardware
> >> register to set the address value of the SPI flash command to be
> >> sent. Hence having this value directly in the right format would avoid to
> >> convert.  
> > 
> > What is the appropriate format? An address encoded in a 64-bit integer
> > does not give any information on how these bytes should be transmitted
> > on the bus. SPI NOR is passing the address in big endian, SPI NAND seems
> > to do the same, but we're not sure this will be the case for all kind of
> > memories or devices using this spi-mem protocol.
> > 
> > Also, by passing it through an integer we limit ourselves to 8 bytes.
> > That should be enough, but who knows :-).
> > 
> > If we go for this loff_t field, we'll have to add an endianness field
> > here and force all drivers to check it.
> >  
> 
> I don't think we need any endianness field. We already use loff_t only,
> without additional endianness paramater, in spi-nor but also in the above
> MTD layer (see 'struct mtd_info') and till now it has just worked fine.

It's not about the MTD layer. Can you predict that all devices will
transmit the address in big-endian? I can't.

> 
> Most, if not all, SPI flash controllers have a dedicated field in one of
> their registers to write the address value into it. Generally, this register
> is written with an integer in the CPU native byte order so no need to convert.

It's not about how you write your register, but in which order address
bytes are transmitted on the SPI wire. Currently, SPI NOR use the MSB
first scheme, but that won't necessarily be the case for other SPI
devices.

> 
> And anyway, if some conversion is ever needed, the SPI flash controller
> driver knows it so we could set as a rule that the integer value for the
> address is always stored in the native CPU byte order. At least most SPI flash
> controller driver won't have to convert from a const u8 * to some unsigned
> integral type more suited to their register accesses.

They will have to at least make sure it's properly encoded: if the
device is expecting the address to be transmitted LSB first (AKA
little-endian), they'll have to swap address bytes before writing the
value to the register.

>  
> >> AFAIK, only m25p80 needs to convert from loff_t to u8 * and only
> >> when using the regular SPI API, ie spi_sync(), as the 'struct
> >> spi_flash_read_messag' already uses some integral type too.  
> > 
> > And I'm not sure this was a good idea.
> >   
> >>
> >>  
> >>> +	} addr;
> >>> +
> >>> +	struct {
> >>> +		u8 nbytes;
> >>> +		u8 buswidth;
> >>> +	} dummy;
> >>> +
> >>> +	struct {
> >>> +		u8 buswidth;
> >>> +		enum spi_mem_data_dir dir;
> >>> +		unsigned int nbytes;
> >>> +		/* buf.{in,out} must be DMA-able. */
> >>> +		union {
> >>> +			void *in;
> >>> +			const void *out;
> >>> +		} buf;
> >>> +	} data;    
> >>
> >> Also, you should add another member in this structure to set the 'type' of
> >> operation for the SPI flash command:
> >> - register read
> >> - register write
> >> - memory read
> >> - memory write
> >> - memory erase
> >> [- SFDP read ? ]
> >> [- OTP read/write ? ]  
> > 
> > No, really, that's not belonging here. It's entirely SPI NOR specific.
> >   
> 
> I disagree with you on that. Except for SFDP of course, this will also apply to
> SPI NAND as well. And this information should be passed down to the SPI (flash)
> controller driver, instead of being lost at the SPI NAND/NOR levels,

I'm not saying we should keep these information at the spi-nor/nand
level, just saying we should find a way to make it look like a generic
solution so that SPI controllers don't have to guess what they should
do based on the memory operation type.

Typically, why should a SPI controller care about whether the operation
is an erase, a reg read or a reg write. From the controller PoV, it's
just an spi-mem operation.
The use cases you want to optimize are read/write user-data, and this
can be done in different ways (direct memory mapping is one solution).

> so the SPI
> controller driver could take the proper actions (data cache flush/invalidation
> for memory operations,

This one has to do with direct mapping, so an API to allow direct
mapping should help address that.

> enabling/disabling on-the-fly data scrambling,

And what's the problem with passing the scramble information on a
per-operation basis and letting the upper layer decide when it's a good
idea to use it or not?

> and other
> controller specific constraints).

Could you be more specific?

I really have the feeling you're reviewing that with your SPI NOR
maintainer eye, while, as I said several times, I'm trying to
address the problem more generically. My intent is not to move every
bits you have in the spi-nor layer to the spi layer. Instead, we can
progressively provide a consistent API that helps you deal with memory
devices (I guess supporting direct memory mapping is the next step if
we want to get the best perfs out of these advanced controllers) in a
device agnostic fashion.

Regards,

Boris

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
  2018-03-05 13:47             ` Boris Brezillon
@ 2018-03-08 14:07               ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-03-08 14:07 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: Boris Brezillon, Yogesh Gaur, Vignesh R, Kamal Dasu,
	Richard Weinberger, linux-spi, Peter Pan, Marek Vasut,
	Frieder Schrempf, Mark Brown, linux-mtd, Rafał Miłecki,
	Sourav Poddar, Brian Norris, David Woodhouse

Hi Cyrille,

On Mon, 5 Mar 2018 14:47:02 +0100
Boris Brezillon <boris.brezillon@bootlin.com> wrote:

> > >     
> > >>> +
> > >>> +/**
> > >>> + * struct spi_mem_op - describes a SPI memory operation
> > >>> + * @cmd.buswidth: number of IO lines used to transmit the command
> > >>> + * @cmd.opcode: operation opcode
> > >>> + * @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.buf: buffer storing the address bytes
> > >>> + * @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
> > >>> + * @data.buswidth: number of IO lanes used to send/receive the data
> > >>> + * @data.dir: direction of the transfer
> > >>> + * @data.buf.in: input buffer
> > >>> + * @data.buf.out: output buffer
> > >>> + */
> > >>> +struct spi_mem_op {
> > >>> +	struct {
> > >>> +		u8 buswidth;
> > >>> +		u8 opcode;
> > >>> +	} cmd;
> > >>> +
> > >>> +	struct {
> > >>> +		u8 nbytes;
> > >>> +		u8 buswidth;
> > >>> +		const u8 *buf;      
> > >>
> > >> For the address value, I would rather use some loff_t type, instead of
> > >> const u8 *. 
> > >> Actually, most (if not all) SPI flash controllers have some hardware
> > >> register to set the address value of the SPI flash command to be
> > >> sent. Hence having this value directly in the right format would avoid to
> > >> convert.    
> > > 
> > > What is the appropriate format? An address encoded in a 64-bit integer
> > > does not give any information on how these bytes should be transmitted
> > > on the bus. SPI NOR is passing the address in big endian, SPI NAND seems
> > > to do the same, but we're not sure this will be the case for all kind of
> > > memories or devices using this spi-mem protocol.
> > > 
> > > Also, by passing it through an integer we limit ourselves to 8 bytes.
> > > That should be enough, but who knows :-).
> > > 
> > > If we go for this loff_t field, we'll have to add an endianness field
> > > here and force all drivers to check it.
> > >    
> > 
> > I don't think we need any endianness field. We already use loff_t only,
> > without additional endianness paramater, in spi-nor but also in the above
> > MTD layer (see 'struct mtd_info') and till now it has just worked fine.  
> 
> It's not about the MTD layer. Can you predict that all devices will
> transmit the address in big-endian? I can't.

If you really want an loff_t (or u64) field here I'll change it (even
if I'm not convinced this is a good idea). In this case we'll just
assume that all devices expect addresses to be sent in big-endian on
the wire (MSB first).


> 
> >    
> > >> AFAIK, only m25p80 needs to convert from loff_t to u8 * and only
> > >> when using the regular SPI API, ie spi_sync(), as the 'struct
> > >> spi_flash_read_messag' already uses some integral type too.    
> > > 
> > > And I'm not sure this was a good idea.
> > >     
> > >>
> > >>    
> > >>> +	} addr;
> > >>> +
> > >>> +	struct {
> > >>> +		u8 nbytes;
> > >>> +		u8 buswidth;
> > >>> +	} dummy;
> > >>> +
> > >>> +	struct {
> > >>> +		u8 buswidth;
> > >>> +		enum spi_mem_data_dir dir;
> > >>> +		unsigned int nbytes;
> > >>> +		/* buf.{in,out} must be DMA-able. */
> > >>> +		union {
> > >>> +			void *in;
> > >>> +			const void *out;
> > >>> +		} buf;
> > >>> +	} data;      
> > >>
> > >> Also, you should add another member in this structure to set the 'type' of
> > >> operation for the SPI flash command:
> > >> - register read
> > >> - register write
> > >> - memory read
> > >> - memory write
> > >> - memory erase
> > >> [- SFDP read ? ]
> > >> [- OTP read/write ? ]    
> > > 
> > > No, really, that's not belonging here. It's entirely SPI NOR specific.
> > >     
> > 
> > I disagree with you on that. Except for SFDP of course, this will also apply to
> > SPI NAND as well. And this information should be passed down to the SPI (flash)
> > controller driver, instead of being lost at the SPI NAND/NOR levels,  
> 
> I'm not saying we should keep these information at the spi-nor/nand
> level, just saying we should find a way to make it look like a generic
> solution so that SPI controllers don't have to guess what they should
> do based on the memory operation type.
> 
> Typically, why should a SPI controller care about whether the operation
> is an erase, a reg read or a reg write. From the controller PoV, it's
> just an spi-mem operation.
> The use cases you want to optimize are read/write user-data, and this
> can be done in different ways (direct memory mapping is one solution).
> 
> > so the SPI
> > controller driver could take the proper actions (data cache flush/invalidation
> > for memory operations,  
> 
> This one has to do with direct mapping, so an API to allow direct
> mapping should help address that.

> 
> > enabling/disabling on-the-fly data scrambling,  
> 
> And what's the problem with passing the scramble information on a
> per-operation basis and letting the upper layer decide when it's a good
> idea to use it or not?
> 
> > and other
> > controller specific constraints).  
> 

Can we leave those additional improvements for later? I mean, we'd
better start passing extra information in the spi_mem_op struct when we
actually know what's needed instead of speculating on what the drivers
will need before having tried to convert them to the new approach.
Actually, I exercised the new interface by converting the fsl-qspi
driver and I didn't need the access-type information you're mentioning
here even though I'm using direct AHB accesses for the read path. I'm
not saying 'never', but 'let's wait until we have a real user'.

If you don't mind, I'd like to send a new version addressing all the
comments I received so far.

Regards,

Boris

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers
@ 2018-03-08 14:07               ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-03-08 14:07 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, linux-mtd, Mark Brown, linux-spi, Peter Pan,
	Frieder Schrempf, Vignesh R, Yogesh Gaur,
	Rafał Miłecki, Kamal Dasu, Sourav Poddar

Hi Cyrille,

On Mon, 5 Mar 2018 14:47:02 +0100
Boris Brezillon <boris.brezillon@bootlin.com> wrote:

> > >     
> > >>> +
> > >>> +/**
> > >>> + * struct spi_mem_op - describes a SPI memory operation
> > >>> + * @cmd.buswidth: number of IO lines used to transmit the command
> > >>> + * @cmd.opcode: operation opcode
> > >>> + * @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.buf: buffer storing the address bytes
> > >>> + * @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
> > >>> + * @data.buswidth: number of IO lanes used to send/receive the data
> > >>> + * @data.dir: direction of the transfer
> > >>> + * @data.buf.in: input buffer
> > >>> + * @data.buf.out: output buffer
> > >>> + */
> > >>> +struct spi_mem_op {
> > >>> +	struct {
> > >>> +		u8 buswidth;
> > >>> +		u8 opcode;
> > >>> +	} cmd;
> > >>> +
> > >>> +	struct {
> > >>> +		u8 nbytes;
> > >>> +		u8 buswidth;
> > >>> +		const u8 *buf;      
> > >>
> > >> For the address value, I would rather use some loff_t type, instead of
> > >> const u8 *. 
> > >> Actually, most (if not all) SPI flash controllers have some hardware
> > >> register to set the address value of the SPI flash command to be
> > >> sent. Hence having this value directly in the right format would avoid to
> > >> convert.    
> > > 
> > > What is the appropriate format? An address encoded in a 64-bit integer
> > > does not give any information on how these bytes should be transmitted
> > > on the bus. SPI NOR is passing the address in big endian, SPI NAND seems
> > > to do the same, but we're not sure this will be the case for all kind of
> > > memories or devices using this spi-mem protocol.
> > > 
> > > Also, by passing it through an integer we limit ourselves to 8 bytes.
> > > That should be enough, but who knows :-).
> > > 
> > > If we go for this loff_t field, we'll have to add an endianness field
> > > here and force all drivers to check it.
> > >    
> > 
> > I don't think we need any endianness field. We already use loff_t only,
> > without additional endianness paramater, in spi-nor but also in the above
> > MTD layer (see 'struct mtd_info') and till now it has just worked fine.  
> 
> It's not about the MTD layer. Can you predict that all devices will
> transmit the address in big-endian? I can't.

If you really want an loff_t (or u64) field here I'll change it (even
if I'm not convinced this is a good idea). In this case we'll just
assume that all devices expect addresses to be sent in big-endian on
the wire (MSB first).


> 
> >    
> > >> AFAIK, only m25p80 needs to convert from loff_t to u8 * and only
> > >> when using the regular SPI API, ie spi_sync(), as the 'struct
> > >> spi_flash_read_messag' already uses some integral type too.    
> > > 
> > > And I'm not sure this was a good idea.
> > >     
> > >>
> > >>    
> > >>> +	} addr;
> > >>> +
> > >>> +	struct {
> > >>> +		u8 nbytes;
> > >>> +		u8 buswidth;
> > >>> +	} dummy;
> > >>> +
> > >>> +	struct {
> > >>> +		u8 buswidth;
> > >>> +		enum spi_mem_data_dir dir;
> > >>> +		unsigned int nbytes;
> > >>> +		/* buf.{in,out} must be DMA-able. */
> > >>> +		union {
> > >>> +			void *in;
> > >>> +			const void *out;
> > >>> +		} buf;
> > >>> +	} data;      
> > >>
> > >> Also, you should add another member in this structure to set the 'type' of
> > >> operation for the SPI flash command:
> > >> - register read
> > >> - register write
> > >> - memory read
> > >> - memory write
> > >> - memory erase
> > >> [- SFDP read ? ]
> > >> [- OTP read/write ? ]    
> > > 
> > > No, really, that's not belonging here. It's entirely SPI NOR specific.
> > >     
> > 
> > I disagree with you on that. Except for SFDP of course, this will also apply to
> > SPI NAND as well. And this information should be passed down to the SPI (flash)
> > controller driver, instead of being lost at the SPI NAND/NOR levels,  
> 
> I'm not saying we should keep these information at the spi-nor/nand
> level, just saying we should find a way to make it look like a generic
> solution so that SPI controllers don't have to guess what they should
> do based on the memory operation type.
> 
> Typically, why should a SPI controller care about whether the operation
> is an erase, a reg read or a reg write. From the controller PoV, it's
> just an spi-mem operation.
> The use cases you want to optimize are read/write user-data, and this
> can be done in different ways (direct memory mapping is one solution).
> 
> > so the SPI
> > controller driver could take the proper actions (data cache flush/invalidation
> > for memory operations,  
> 
> This one has to do with direct mapping, so an API to allow direct
> mapping should help address that.

> 
> > enabling/disabling on-the-fly data scrambling,  
> 
> And what's the problem with passing the scramble information on a
> per-operation basis and letting the upper layer decide when it's a good
> idea to use it or not?
> 
> > and other
> > controller specific constraints).  
> 

Can we leave those additional improvements for later? I mean, we'd
better start passing extra information in the spi_mem_op struct when we
actually know what's needed instead of speculating on what the drivers
will need before having tried to convert them to the new approach.
Actually, I exercised the new interface by converting the fsl-qspi
driver and I didn't need the access-type information you're mentioning
here even though I'm using direct AHB accesses for the read path. I'm
not saying 'never', but 'let's wait until we have a real user'.

If you don't mind, I'd like to send a new version addressing all the
comments I received so far.

Regards,

Boris

-- 
Boris Brezillon, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

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

* RE: [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API
  2018-02-05 23:21     ` Boris Brezillon
@ 2018-06-11  6:25       ` Yogesh Narayan Gaur
  -1 siblings, 0 replies; 92+ messages in thread
From: Yogesh Narayan Gaur @ 2018-06-11  6:25 UTC (permalink / raw)
  To: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen, linux-mtd,
	Mark Brown, linux-spi
  Cc: Vignesh R, Kamal Dasu, Peter Pan, Frieder Schrempf,
	Rafał Miłecki, Sourav Poddar

Hi Boris,

Issue which I am getting in spi-fsl-qspi.c regarding write is happening due to the changes been pushed by this patch series.

Comments inline, comments holds true for the final pushed change.

--
Regards
Yogesh Gaur

-----Original Message-----
From: Boris Brezillon [mailto:boris.brezillon@bootlin.com] 
Sent: Tuesday, February 6, 2018 4:51 AM
To: David Woodhouse <dwmw2@infradead.org>; Brian Norris <computersforpeace@gmail.com>; Boris Brezillon <boris.brezillon@free-electrons.com>; Marek Vasut <marek.vasut@gmail.com>; Richard Weinberger <richard@nod.at>; Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>; linux-mtd@lists.infradead.org; Mark Brown <broonie@kernel.org>; linux-spi@vger.kernel.org
Cc: Peter Pan <peterpansjtu@gmail.com>; Frieder Schrempf <frieder.schrempf@exceet.de>; Vignesh R <vigneshr@ti.com>; Yogesh Narayan Gaur <yogeshnarayan.gaur@nxp.com>; Rafał Miłecki <rafal@milecki.pl>; Kamal Dasu <kdasu.kdev@gmail.com>; Sourav Poddar <sourav.poddar@ti.com>
Subject: [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API

From: Boris Brezillon <boris.brezillon@free-electrons.com>

The spi_mem_xxx() API has been introduced to replace the
spi_flash_read() one. Make use of it so we can get rid of spi_flash_read().

Note that using spi_mem_xx() also simplifies the code because this API takes care of using the regular spi_sync() interface when the optimized
->mem_ops interface is not implemented by the controller.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/mtd/devices/m25p80.c | 240 ++++++++++++++++---------------------------
 1 file changed, 88 insertions(+), 152 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index a4e18f6aaa33..3b17425d5e6f 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -29,7 +29,7 @@
 
 #define	MAX_CMD_SIZE		6
 struct m25p {
-	struct spi_device	*spi;
+	struct spi_mem		*spimem;
 	struct spi_nor		spi_nor;
 	u8			command[MAX_CMD_SIZE];
 };
@@ -37,97 +37,80 @@ struct m25p {
 static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)  {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
+	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
+					  SPI_MEM_OP_NO_ADDRS,
+					  SPI_MEM_OP_NO_DUMMY,
+					  SPI_MEM_OP_DATA_IN(len, val, 1));
 	int ret;
 
-	ret = spi_write_then_read(spi, &code, 1, val, len);
+	ret = spi_mem_exec_op(flash->spimem, &op);
 	if (ret < 0)
-		dev_err(&spi->dev, "error %d reading %x\n", ret, code);
+		dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret,
+			code);
 
 	return ret;
 }
 
-static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
+static void m25p_offs2addr(struct spi_nor *nor, unsigned int offs, u8 
+*addrs)
 {
-	/* opcode is in cmd[0] */
-	cmd[1] = addr >> (nor->addr_width * 8 -  8);
-	cmd[2] = addr >> (nor->addr_width * 8 - 16);
-	cmd[3] = addr >> (nor->addr_width * 8 - 24);
-	cmd[4] = addr >> (nor->addr_width * 8 - 32);
-}
-
-static int m25p_cmdsz(struct spi_nor *nor) -{
-	return 1 + nor->addr_width;
+	addrs[0] = offs >> (nor->addr_width * 8 -  8);
+	addrs[1] = offs >> (nor->addr_width * 8 - 16);
+	addrs[2] = offs >> (nor->addr_width * 8 - 24);
+	addrs[3] = offs >> (nor->addr_width * 8 - 32);
 }
 
 static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)  {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
+	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
+					  SPI_MEM_OP_NO_ADDRS,
+					  SPI_MEM_OP_NO_DUMMY,
+					  SPI_MEM_OP_DATA_OUT(len, buf, 1));
 
-	flash->command[0] = opcode;
-	if (buf)
-		memcpy(&flash->command[1], buf, len);
-
-	return spi_write(spi, flash->command, len + 1);
+	return spi_mem_exec_op(flash->spimem, &op);
 }
 
 static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
 			    const u_char *buf)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
-	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
-	struct spi_transfer t[3] = {};
-	struct spi_message m;
-	int cmd_sz = m25p_cmdsz(nor);
-	ssize_t ret;
+	u8 addrs[4];
+	struct spi_mem_op op =
+			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
+				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
+				   SPI_MEM_OP_DUMMY(0, 1),
+				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
+	size_t remaining = len;
+	int ret;
 
 	/* get transfer protocols. */
-	inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto);
-	addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto);
-	data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto);
-
-	spi_message_init(&m);
+	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)
-		cmd_sz = 1;
-
-	flash->command[0] = nor->program_opcode;
-	m25p_addr2cmd(nor, to, flash->command);
+		op.addr.nbytes = 0;
 
-	t[0].tx_buf = flash->command;
-	t[0].tx_nbits = inst_nbits;
-	t[0].len = cmd_sz;
-	spi_message_add_tail(&t[0], &m);
+	while (remaining) {
+		if (op.addr.nbytes)
+			m25p_offs2addr(nor, to, addrs);
 
-	/* split the op code and address bytes into two transfers if needed. */
-	data_idx = 1;
-	if (addr_nbits != inst_nbits) {
-		t[0].len = 1;
+		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+		ret = spi_mem_adjust_op_size(flash->spimem, &op);
+		if (ret)
+			return ret;
 
-		t[1].tx_buf = &flash->command[1];
-		t[1].tx_nbits = addr_nbits;
-		t[1].len = cmd_sz - 1;
-		spi_message_add_tail(&t[1], &m);
+		ret = spi_mem_exec_op(flash->spimem, &op);
+		if (ret)
+			return ret;
 
-		data_idx = 2;
+		to += op.data.nbytes;
+		remaining -= op.data.nbytes;
+		op.data.buf.out += op.data.nbytes;

For NOR flashes, WRITE command is being send in order as below
Write Enable command
Flash Write command
Status Register command

But for case when Write data size is more than op.data.nbytes then for remaining data size chunk, we are just sending the FLASH WRITE Command and this is the reason I am seeing failure after 64 byte data size.


 	}
 
-	t[data_idx].tx_buf = buf;
-	t[data_idx].tx_nbits = data_nbits;
-	t[data_idx].len = len;
-	spi_message_add_tail(&t[data_idx], &m);
-
-	ret = spi_sync(spi, &m);
-	if (ret)
-		return ret;
-
-	ret = m.actual_length - cmd_sz;
-	if (ret < 0)
-		return -EIO;
-	return ret;
+	return len;
 }
 
 /*
@@ -138,92 +121,42 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
 			   u_char *buf)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
-	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
-	struct spi_transfer t[3];
-	struct spi_message m;
-	unsigned int dummy = nor->read_dummy;
-	ssize_t ret;
-	int cmd_sz;
+	u8 addrs[4];
+	struct spi_mem_op op =
+			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
+				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
+				   SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
+				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
+	size_t remaining = len;
+	int ret;
 
 	/* get transfer protocols. */
-	inst_nbits = spi_nor_get_protocol_inst_nbits(nor->read_proto);
-	addr_nbits = spi_nor_get_protocol_addr_nbits(nor->read_proto);
-	data_nbits = spi_nor_get_protocol_data_nbits(nor->read_proto);
+	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 */
-	dummy = (dummy * addr_nbits) / 8;
-
-	if (spi_flash_read_supported(spi)) {
-		struct spi_flash_read_message msg;
+	op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
 
-		memset(&msg, 0, sizeof(msg));
+	while (remaining) {
+		m25p_offs2addr(nor, from, addrs);
 
-		msg.buf = buf;
-		msg.from = from;
-		msg.len = len;
-		msg.read_opcode = nor->read_opcode;
-		msg.addr_width = nor->addr_width;
-		msg.dummy_bytes = dummy;
-		msg.opcode_nbits = inst_nbits;
-		msg.addr_nbits = addr_nbits;
-		msg.data_nbits = data_nbits;
-
-		ret = spi_flash_read(spi, &msg);
-		if (ret < 0)
+		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+		ret = spi_mem_adjust_op_size(flash->spimem, &op);
+		if (ret)
 			return ret;
-		return msg.retlen;
-	}
-
-	spi_message_init(&m);
-	memset(t, 0, (sizeof t));
-
-	flash->command[0] = nor->read_opcode;
-	m25p_addr2cmd(nor, from, flash->command);
-
-	t[0].tx_buf = flash->command;
-	t[0].tx_nbits = inst_nbits;
-	t[0].len = m25p_cmdsz(nor) + dummy;
-	spi_message_add_tail(&t[0], &m);
-
-	/*
-	 * Set all dummy/mode cycle bits to avoid sending some manufacturer
-	 * specific pattern, which might make the memory enter its Continuous
-	 * Read mode by mistake.
-	 * Based on the different mode cycle bit patterns listed and described
-	 * in the JESD216B specification, the 0xff value works for all memories
-	 * and all manufacturers.
-	 */
-	cmd_sz = t[0].len;
-	memset(flash->command + cmd_sz - dummy, 0xff, dummy);
 
-	/* split the op code and address bytes into two transfers if needed. */
-	data_idx = 1;
-	if (addr_nbits != inst_nbits) {
-		t[0].len = 1;
-
-		t[1].tx_buf = &flash->command[1];
-		t[1].tx_nbits = addr_nbits;
-		t[1].len = cmd_sz - 1;
-		spi_message_add_tail(&t[1], &m);
+		ret = spi_mem_exec_op(flash->spimem, &op);
+		if (ret)
+			return ret;
 
-		data_idx = 2;
+		from += op.data.nbytes;
+		remaining -= op.data.nbytes;
+		op.data.buf.in += op.data.nbytes;
 	}
 
-	t[data_idx].rx_buf = buf;
-	t[data_idx].rx_nbits = data_nbits;
-	t[data_idx].len = min3(len, spi_max_transfer_size(spi),
-			       spi_max_message_size(spi) - cmd_sz);
-	spi_message_add_tail(&t[data_idx], &m);
-
-	ret = spi_sync(spi, &m);
-	if (ret)
-		return ret;
-
-	ret = m.actual_length - cmd_sz;
-	if (ret < 0)
-		return -EIO;
-	return ret;
+	return len;
 }
 
 /*
@@ -231,8 +164,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
  * matches what the READ command supports, at least until this driver
  * understands FAST_READ (for clocks over 25 MHz).
  */
-static int m25p_probe(struct spi_device *spi)
+static int m25p_probe(struct spi_mem *spimem)
 {
+	struct spi_device *spi = spimem->spi;
 	struct flash_platform_data	*data;
 	struct m25p *flash;
 	struct spi_nor *nor;
@@ -244,9 +178,9 @@ static int m25p_probe(struct spi_device *spi)
 	char *flash_name;
 	int ret;
 
-	data = dev_get_platdata(&spi->dev);
+	data = dev_get_platdata(&spimem->spi->dev);
 
-	flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
+	flash = devm_kzalloc(&spimem->spi->dev, sizeof(*flash), GFP_KERNEL);
 	if (!flash)
 		return -ENOMEM;
 
@@ -258,12 +192,12 @@ static int m25p_probe(struct spi_device *spi)
 	nor->write_reg = m25p80_write_reg;
 	nor->read_reg = m25p80_read_reg;
 
-	nor->dev = &spi->dev;
+	nor->dev = &spimem->spi->dev;
 	spi_nor_set_flash_node(nor, spi->dev.of_node);
 	nor->priv = flash;
 
 	spi_set_drvdata(spi, flash);
-	flash->spi = spi;
+	flash->spimem = spimem;
 
 	if (spi->mode & SPI_RX_QUAD) {
 		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; @@ -303,9 +237,9 @@ static int m25p_probe(struct spi_device *spi)  }
 
 
-static int m25p_remove(struct spi_device *spi)
+static int m25p_remove(struct spi_mem *spimem)
 {
-	struct m25p	*flash = spi_get_drvdata(spi);
+	struct m25p	*flash = spi_mem_get_drvdata(spimem);
 
 	spi_nor_restore(&flash->spi_nor);
 
@@ -313,9 +247,9 @@ static int m25p_remove(struct spi_device *spi)
 	return mtd_device_unregister(&flash->spi_nor.mtd);
 }
 
-static void m25p_shutdown(struct spi_device *spi)
+static void m25p_shutdown(struct spi_mem *spimem)
 {
-	struct m25p *flash = spi_get_drvdata(spi);
+	struct m25p *flash = spi_mem_get_drvdata(spimem);
 
 	spi_nor_restore(&flash->spi_nor);
 }
@@ -386,12 +320,14 @@ static const struct of_device_id m25p_of_table[] = {  };  MODULE_DEVICE_TABLE(of, m25p_of_table);
 
-static struct spi_driver m25p80_driver = {
-	.driver = {
-		.name	= "m25p80",
-		.of_match_table = m25p_of_table,
+static struct spi_mem_driver m25p80_driver = {
+	.spidrv = {
+		.driver = {
+			.name	= "m25p80",
+			.of_match_table = m25p_of_table,
+		},
+		.id_table	= m25p_ids,
 	},
-	.id_table	= m25p_ids,
 	.probe	= m25p_probe,
 	.remove	= m25p_remove,
 	.shutdown	= m25p_shutdown,
@@ -402,7 +338,7 @@ static struct spi_driver m25p80_driver = {
 	 */
 };
 
-module_spi_driver(m25p80_driver);
+module_spi_mem_driver(m25p80_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Mike Lavender");
--
2.14.1


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* RE: [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API
@ 2018-06-11  6:25       ` Yogesh Narayan Gaur
  0 siblings, 0 replies; 92+ messages in thread
From: Yogesh Narayan Gaur @ 2018-06-11  6:25 UTC (permalink / raw)
  To: Boris Brezillon, David Woodhouse, Brian Norris, Boris Brezillon,
	Marek Vasut, Richard Weinberger, Cyrille Pitchen, linux-mtd,
	Mark Brown, linux-spi
  Cc: Peter Pan, Frieder Schrempf, Vignesh R, Rafał Miłecki,
	Kamal Dasu, Sourav Poddar

Hi Boris,

Issue which I am getting in spi-fsl-qspi.c regarding write is happening due to the changes been pushed by this patch series.

Comments inline, comments holds true for the final pushed change.

--
Regards
Yogesh Gaur

-----Original Message-----
From: Boris Brezillon [mailto:boris.brezillon@bootlin.com] 
Sent: Tuesday, February 6, 2018 4:51 AM
To: David Woodhouse <dwmw2@infradead.org>; Brian Norris <computersforpeace@gmail.com>; Boris Brezillon <boris.brezillon@free-electrons.com>; Marek Vasut <marek.vasut@gmail.com>; Richard Weinberger <richard@nod.at>; Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>; linux-mtd@lists.infradead.org; Mark Brown <broonie@kernel.org>; linux-spi@vger.kernel.org
Cc: Peter Pan <peterpansjtu@gmail.com>; Frieder Schrempf <frieder.schrempf@exceet.de>; Vignesh R <vigneshr@ti.com>; Yogesh Narayan Gaur <yogeshnarayan.gaur@nxp.com>; Rafał Miłecki <rafal@milecki.pl>; Kamal Dasu <kdasu.kdev@gmail.com>; Sourav Poddar <sourav.poddar@ti.com>
Subject: [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API

From: Boris Brezillon <boris.brezillon@free-electrons.com>

The spi_mem_xxx() API has been introduced to replace the
spi_flash_read() one. Make use of it so we can get rid of spi_flash_read().

Note that using spi_mem_xx() also simplifies the code because this API takes care of using the regular spi_sync() interface when the optimized
->mem_ops interface is not implemented by the controller.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/mtd/devices/m25p80.c | 240 ++++++++++++++++---------------------------
 1 file changed, 88 insertions(+), 152 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index a4e18f6aaa33..3b17425d5e6f 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -29,7 +29,7 @@
 
 #define	MAX_CMD_SIZE		6
 struct m25p {
-	struct spi_device	*spi;
+	struct spi_mem		*spimem;
 	struct spi_nor		spi_nor;
 	u8			command[MAX_CMD_SIZE];
 };
@@ -37,97 +37,80 @@ struct m25p {
 static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)  {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
+	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
+					  SPI_MEM_OP_NO_ADDRS,
+					  SPI_MEM_OP_NO_DUMMY,
+					  SPI_MEM_OP_DATA_IN(len, val, 1));
 	int ret;
 
-	ret = spi_write_then_read(spi, &code, 1, val, len);
+	ret = spi_mem_exec_op(flash->spimem, &op);
 	if (ret < 0)
-		dev_err(&spi->dev, "error %d reading %x\n", ret, code);
+		dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret,
+			code);
 
 	return ret;
 }
 
-static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
+static void m25p_offs2addr(struct spi_nor *nor, unsigned int offs, u8 
+*addrs)
 {
-	/* opcode is in cmd[0] */
-	cmd[1] = addr >> (nor->addr_width * 8 -  8);
-	cmd[2] = addr >> (nor->addr_width * 8 - 16);
-	cmd[3] = addr >> (nor->addr_width * 8 - 24);
-	cmd[4] = addr >> (nor->addr_width * 8 - 32);
-}
-
-static int m25p_cmdsz(struct spi_nor *nor) -{
-	return 1 + nor->addr_width;
+	addrs[0] = offs >> (nor->addr_width * 8 -  8);
+	addrs[1] = offs >> (nor->addr_width * 8 - 16);
+	addrs[2] = offs >> (nor->addr_width * 8 - 24);
+	addrs[3] = offs >> (nor->addr_width * 8 - 32);
 }
 
 static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)  {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
+	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
+					  SPI_MEM_OP_NO_ADDRS,
+					  SPI_MEM_OP_NO_DUMMY,
+					  SPI_MEM_OP_DATA_OUT(len, buf, 1));
 
-	flash->command[0] = opcode;
-	if (buf)
-		memcpy(&flash->command[1], buf, len);
-
-	return spi_write(spi, flash->command, len + 1);
+	return spi_mem_exec_op(flash->spimem, &op);
 }
 
 static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
 			    const u_char *buf)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
-	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
-	struct spi_transfer t[3] = {};
-	struct spi_message m;
-	int cmd_sz = m25p_cmdsz(nor);
-	ssize_t ret;
+	u8 addrs[4];
+	struct spi_mem_op op =
+			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
+				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
+				   SPI_MEM_OP_DUMMY(0, 1),
+				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
+	size_t remaining = len;
+	int ret;
 
 	/* get transfer protocols. */
-	inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto);
-	addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto);
-	data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto);
-
-	spi_message_init(&m);
+	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)
-		cmd_sz = 1;
-
-	flash->command[0] = nor->program_opcode;
-	m25p_addr2cmd(nor, to, flash->command);
+		op.addr.nbytes = 0;
 
-	t[0].tx_buf = flash->command;
-	t[0].tx_nbits = inst_nbits;
-	t[0].len = cmd_sz;
-	spi_message_add_tail(&t[0], &m);
+	while (remaining) {
+		if (op.addr.nbytes)
+			m25p_offs2addr(nor, to, addrs);
 
-	/* split the op code and address bytes into two transfers if needed. */
-	data_idx = 1;
-	if (addr_nbits != inst_nbits) {
-		t[0].len = 1;
+		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+		ret = spi_mem_adjust_op_size(flash->spimem, &op);
+		if (ret)
+			return ret;
 
-		t[1].tx_buf = &flash->command[1];
-		t[1].tx_nbits = addr_nbits;
-		t[1].len = cmd_sz - 1;
-		spi_message_add_tail(&t[1], &m);
+		ret = spi_mem_exec_op(flash->spimem, &op);
+		if (ret)
+			return ret;
 
-		data_idx = 2;
+		to += op.data.nbytes;
+		remaining -= op.data.nbytes;
+		op.data.buf.out += op.data.nbytes;

For NOR flashes, WRITE command is being send in order as below
Write Enable command
Flash Write command
Status Register command

But for case when Write data size is more than op.data.nbytes then for remaining data size chunk, we are just sending the FLASH WRITE Command and this is the reason I am seeing failure after 64 byte data size.


 	}
 
-	t[data_idx].tx_buf = buf;
-	t[data_idx].tx_nbits = data_nbits;
-	t[data_idx].len = len;
-	spi_message_add_tail(&t[data_idx], &m);
-
-	ret = spi_sync(spi, &m);
-	if (ret)
-		return ret;
-
-	ret = m.actual_length - cmd_sz;
-	if (ret < 0)
-		return -EIO;
-	return ret;
+	return len;
 }
 
 /*
@@ -138,92 +121,42 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
 			   u_char *buf)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
-	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
-	struct spi_transfer t[3];
-	struct spi_message m;
-	unsigned int dummy = nor->read_dummy;
-	ssize_t ret;
-	int cmd_sz;
+	u8 addrs[4];
+	struct spi_mem_op op =
+			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
+				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
+				   SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
+				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
+	size_t remaining = len;
+	int ret;
 
 	/* get transfer protocols. */
-	inst_nbits = spi_nor_get_protocol_inst_nbits(nor->read_proto);
-	addr_nbits = spi_nor_get_protocol_addr_nbits(nor->read_proto);
-	data_nbits = spi_nor_get_protocol_data_nbits(nor->read_proto);
+	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 */
-	dummy = (dummy * addr_nbits) / 8;
-
-	if (spi_flash_read_supported(spi)) {
-		struct spi_flash_read_message msg;
+	op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
 
-		memset(&msg, 0, sizeof(msg));
+	while (remaining) {
+		m25p_offs2addr(nor, from, addrs);
 
-		msg.buf = buf;
-		msg.from = from;
-		msg.len = len;
-		msg.read_opcode = nor->read_opcode;
-		msg.addr_width = nor->addr_width;
-		msg.dummy_bytes = dummy;
-		msg.opcode_nbits = inst_nbits;
-		msg.addr_nbits = addr_nbits;
-		msg.data_nbits = data_nbits;
-
-		ret = spi_flash_read(spi, &msg);
-		if (ret < 0)
+		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+		ret = spi_mem_adjust_op_size(flash->spimem, &op);
+		if (ret)
 			return ret;
-		return msg.retlen;
-	}
-
-	spi_message_init(&m);
-	memset(t, 0, (sizeof t));
-
-	flash->command[0] = nor->read_opcode;
-	m25p_addr2cmd(nor, from, flash->command);
-
-	t[0].tx_buf = flash->command;
-	t[0].tx_nbits = inst_nbits;
-	t[0].len = m25p_cmdsz(nor) + dummy;
-	spi_message_add_tail(&t[0], &m);
-
-	/*
-	 * Set all dummy/mode cycle bits to avoid sending some manufacturer
-	 * specific pattern, which might make the memory enter its Continuous
-	 * Read mode by mistake.
-	 * Based on the different mode cycle bit patterns listed and described
-	 * in the JESD216B specification, the 0xff value works for all memories
-	 * and all manufacturers.
-	 */
-	cmd_sz = t[0].len;
-	memset(flash->command + cmd_sz - dummy, 0xff, dummy);
 
-	/* split the op code and address bytes into two transfers if needed. */
-	data_idx = 1;
-	if (addr_nbits != inst_nbits) {
-		t[0].len = 1;
-
-		t[1].tx_buf = &flash->command[1];
-		t[1].tx_nbits = addr_nbits;
-		t[1].len = cmd_sz - 1;
-		spi_message_add_tail(&t[1], &m);
+		ret = spi_mem_exec_op(flash->spimem, &op);
+		if (ret)
+			return ret;
 
-		data_idx = 2;
+		from += op.data.nbytes;
+		remaining -= op.data.nbytes;
+		op.data.buf.in += op.data.nbytes;
 	}
 
-	t[data_idx].rx_buf = buf;
-	t[data_idx].rx_nbits = data_nbits;
-	t[data_idx].len = min3(len, spi_max_transfer_size(spi),
-			       spi_max_message_size(spi) - cmd_sz);
-	spi_message_add_tail(&t[data_idx], &m);
-
-	ret = spi_sync(spi, &m);
-	if (ret)
-		return ret;
-
-	ret = m.actual_length - cmd_sz;
-	if (ret < 0)
-		return -EIO;
-	return ret;
+	return len;
 }
 
 /*
@@ -231,8 +164,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
  * matches what the READ command supports, at least until this driver
  * understands FAST_READ (for clocks over 25 MHz).
  */
-static int m25p_probe(struct spi_device *spi)
+static int m25p_probe(struct spi_mem *spimem)
 {
+	struct spi_device *spi = spimem->spi;
 	struct flash_platform_data	*data;
 	struct m25p *flash;
 	struct spi_nor *nor;
@@ -244,9 +178,9 @@ static int m25p_probe(struct spi_device *spi)
 	char *flash_name;
 	int ret;
 
-	data = dev_get_platdata(&spi->dev);
+	data = dev_get_platdata(&spimem->spi->dev);
 
-	flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
+	flash = devm_kzalloc(&spimem->spi->dev, sizeof(*flash), GFP_KERNEL);
 	if (!flash)
 		return -ENOMEM;
 
@@ -258,12 +192,12 @@ static int m25p_probe(struct spi_device *spi)
 	nor->write_reg = m25p80_write_reg;
 	nor->read_reg = m25p80_read_reg;
 
-	nor->dev = &spi->dev;
+	nor->dev = &spimem->spi->dev;
 	spi_nor_set_flash_node(nor, spi->dev.of_node);
 	nor->priv = flash;
 
 	spi_set_drvdata(spi, flash);
-	flash->spi = spi;
+	flash->spimem = spimem;
 
 	if (spi->mode & SPI_RX_QUAD) {
 		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; @@ -303,9 +237,9 @@ static int m25p_probe(struct spi_device *spi)  }
 
 
-static int m25p_remove(struct spi_device *spi)
+static int m25p_remove(struct spi_mem *spimem)
 {
-	struct m25p	*flash = spi_get_drvdata(spi);
+	struct m25p	*flash = spi_mem_get_drvdata(spimem);
 
 	spi_nor_restore(&flash->spi_nor);
 
@@ -313,9 +247,9 @@ static int m25p_remove(struct spi_device *spi)
 	return mtd_device_unregister(&flash->spi_nor.mtd);
 }
 
-static void m25p_shutdown(struct spi_device *spi)
+static void m25p_shutdown(struct spi_mem *spimem)
 {
-	struct m25p *flash = spi_get_drvdata(spi);
+	struct m25p *flash = spi_mem_get_drvdata(spimem);
 
 	spi_nor_restore(&flash->spi_nor);
 }
@@ -386,12 +320,14 @@ static const struct of_device_id m25p_of_table[] = {  };  MODULE_DEVICE_TABLE(of, m25p_of_table);
 
-static struct spi_driver m25p80_driver = {
-	.driver = {
-		.name	= "m25p80",
-		.of_match_table = m25p_of_table,
+static struct spi_mem_driver m25p80_driver = {
+	.spidrv = {
+		.driver = {
+			.name	= "m25p80",
+			.of_match_table = m25p_of_table,
+		},
+		.id_table	= m25p_ids,
 	},
-	.id_table	= m25p_ids,
 	.probe	= m25p_probe,
 	.remove	= m25p_remove,
 	.shutdown	= m25p_shutdown,
@@ -402,7 +338,7 @@ static struct spi_driver m25p80_driver = {
 	 */
 };
 
-module_spi_driver(m25p80_driver);
+module_spi_mem_driver(m25p80_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Mike Lavender");
--
2.14.1

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

* Re: [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API
  2018-06-11  6:25       ` Yogesh Narayan Gaur
@ 2018-06-11  7:35         ` Boris Brezillon
  -1 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-06-11  7:35 UTC (permalink / raw)
  To: Yogesh Narayan Gaur
  Cc: Boris Brezillon, Vignesh R, Kamal Dasu, Richard Weinberger,
	linux-spi, Peter Pan, Marek Vasut, Frieder Schrempf, Mark Brown,
	linux-mtd, Cyrille Pitchen, Rafał Miłecki,
	Sourav Poddar, Brian Norris, David Woodhouse

Hi Yogesh,

On Mon, 11 Jun 2018 06:25:02 +0000
Yogesh Narayan Gaur <yogeshnarayan.gaur@nxp.com> wrote:


>  static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>  			    const u_char *buf)
>  {
>  	struct m25p *flash = nor->priv;
> -	struct spi_device *spi = flash->spi;
> -	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
> -	struct spi_transfer t[3] = {};
> -	struct spi_message m;
> -	int cmd_sz = m25p_cmdsz(nor);
> -	ssize_t ret;
> +	u8 addrs[4];
> +	struct spi_mem_op op =
> +			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
> +				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
> +				   SPI_MEM_OP_DUMMY(0, 1),
> +				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
> +	size_t remaining = len;
> +	int ret;
>  
>  	/* get transfer protocols. */
> -	inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto);
> -	addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto);
> -	data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto);
> -
> -	spi_message_init(&m);
> +	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)
> -		cmd_sz = 1;
> -
> -	flash->command[0] = nor->program_opcode;
> -	m25p_addr2cmd(nor, to, flash->command);
> +		op.addr.nbytes = 0;
>  
> -	t[0].tx_buf = flash->command;
> -	t[0].tx_nbits = inst_nbits;
> -	t[0].len = cmd_sz;
> -	spi_message_add_tail(&t[0], &m);
> +	while (remaining) {
> +		if (op.addr.nbytes)
> +			m25p_offs2addr(nor, to, addrs);
>  
> -	/* split the op code and address bytes into two transfers if needed. */
> -	data_idx = 1;
> -	if (addr_nbits != inst_nbits) {
> -		t[0].len = 1;
> +		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
> +		ret = spi_mem_adjust_op_size(flash->spimem, &op);
> +		if (ret)
> +			return ret;
>  
> -		t[1].tx_buf = &flash->command[1];
> -		t[1].tx_nbits = addr_nbits;
> -		t[1].len = cmd_sz - 1;
> -		spi_message_add_tail(&t[1], &m);
> +		ret = spi_mem_exec_op(flash->spimem, &op);
> +		if (ret)
> +			return ret;
>  
> -		data_idx = 2;
> +		to += op.data.nbytes;
> +		remaining -= op.data.nbytes;
> +		op.data.buf.out += op.data.nbytes;
> 
> For NOR flashes, WRITE command is being send in order as below
> Write Enable command
> Flash Write command
> Status Register command
> 
> But for case when Write data size is more than op.data.nbytes then for
> remaining data size chunk, we are just sending the FLASH WRITE Command
> and this is the reason I am seeing failure after 64 byte data size.
> 

Hm, I guess we'll have to revive this patch [1] (see below for a
modified version based on the spi-mem changes).

Regards,

Boris

[1]https://patchwork.ozlabs.org/patch/905507/

--->8---
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index e84563d2067f..f2403b36649c 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -84,22 +84,16 @@ static ssize_t m25p80_write(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;
 
-       while (remaining) {
-               op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
-               ret = spi_mem_adjust_op_size(flash->spimem, &op);
-               if (ret)
-                       return ret;
-
-               ret = spi_mem_exec_op(flash->spimem, &op);
-               if (ret)
-                       return ret;
+       op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+       ret = spi_mem_adjust_op_size(flash->spimem, &op);
+       if (ret)
+               return ret;
 
-               op.addr.val += op.data.nbytes;
-               remaining -= op.data.nbytes;
-               op.data.buf.out += op.data.nbytes;
-       }
+       ret = spi_mem_exec_op(flash->spimem, &op);
+       if (ret)
+               return ret;
 
-       return len;
+       return op.data.nbytes;
 }
 
 /*
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 5bfa36e95f35..3e635430bfde 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1431,13 +1431,6 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
                        goto write_err;
                *retlen += written;
                i += written;
-               if (written != page_remain) {
-                       dev_err(nor->dev,
-                               "While writing %zu bytes written %zd bytes\n",
-                               page_remain, written);
-                       ret = -EIO;
-                       goto write_err;
-               }
        }
 
 write_err:


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API
@ 2018-06-11  7:35         ` Boris Brezillon
  0 siblings, 0 replies; 92+ messages in thread
From: Boris Brezillon @ 2018-06-11  7:35 UTC (permalink / raw)
  To: Yogesh Narayan Gaur
  Cc: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
	Richard Weinberger, Cyrille Pitchen, linux-mtd, Mark Brown,
	linux-spi, Vignesh R, Kamal Dasu, Peter Pan, Frieder Schrempf,
	Rafał Miłecki, Sourav Poddar

Hi Yogesh,

On Mon, 11 Jun 2018 06:25:02 +0000
Yogesh Narayan Gaur <yogeshnarayan.gaur@nxp.com> wrote:


>  static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>  			    const u_char *buf)
>  {
>  	struct m25p *flash = nor->priv;
> -	struct spi_device *spi = flash->spi;
> -	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
> -	struct spi_transfer t[3] = {};
> -	struct spi_message m;
> -	int cmd_sz = m25p_cmdsz(nor);
> -	ssize_t ret;
> +	u8 addrs[4];
> +	struct spi_mem_op op =
> +			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
> +				   SPI_MEM_OP_ADDRS(nor->addr_width, addrs, 1),
> +				   SPI_MEM_OP_DUMMY(0, 1),
> +				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
> +	size_t remaining = len;
> +	int ret;
>  
>  	/* get transfer protocols. */
> -	inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto);
> -	addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto);
> -	data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto);
> -
> -	spi_message_init(&m);
> +	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)
> -		cmd_sz = 1;
> -
> -	flash->command[0] = nor->program_opcode;
> -	m25p_addr2cmd(nor, to, flash->command);
> +		op.addr.nbytes = 0;
>  
> -	t[0].tx_buf = flash->command;
> -	t[0].tx_nbits = inst_nbits;
> -	t[0].len = cmd_sz;
> -	spi_message_add_tail(&t[0], &m);
> +	while (remaining) {
> +		if (op.addr.nbytes)
> +			m25p_offs2addr(nor, to, addrs);
>  
> -	/* split the op code and address bytes into two transfers if needed. */
> -	data_idx = 1;
> -	if (addr_nbits != inst_nbits) {
> -		t[0].len = 1;
> +		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
> +		ret = spi_mem_adjust_op_size(flash->spimem, &op);
> +		if (ret)
> +			return ret;
>  
> -		t[1].tx_buf = &flash->command[1];
> -		t[1].tx_nbits = addr_nbits;
> -		t[1].len = cmd_sz - 1;
> -		spi_message_add_tail(&t[1], &m);
> +		ret = spi_mem_exec_op(flash->spimem, &op);
> +		if (ret)
> +			return ret;
>  
> -		data_idx = 2;
> +		to += op.data.nbytes;
> +		remaining -= op.data.nbytes;
> +		op.data.buf.out += op.data.nbytes;
> 
> For NOR flashes, WRITE command is being send in order as below
> Write Enable command
> Flash Write command
> Status Register command
> 
> But for case when Write data size is more than op.data.nbytes then for
> remaining data size chunk, we are just sending the FLASH WRITE Command
> and this is the reason I am seeing failure after 64 byte data size.
> 

Hm, I guess we'll have to revive this patch [1] (see below for a
modified version based on the spi-mem changes).

Regards,

Boris

[1]https://patchwork.ozlabs.org/patch/905507/

--->8---
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index e84563d2067f..f2403b36649c 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -84,22 +84,16 @@ static ssize_t m25p80_write(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;
 
-       while (remaining) {
-               op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
-               ret = spi_mem_adjust_op_size(flash->spimem, &op);
-               if (ret)
-                       return ret;
-
-               ret = spi_mem_exec_op(flash->spimem, &op);
-               if (ret)
-                       return ret;
+       op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+       ret = spi_mem_adjust_op_size(flash->spimem, &op);
+       if (ret)
+               return ret;
 
-               op.addr.val += op.data.nbytes;
-               remaining -= op.data.nbytes;
-               op.data.buf.out += op.data.nbytes;
-       }
+       ret = spi_mem_exec_op(flash->spimem, &op);
+       if (ret)
+               return ret;
 
-       return len;
+       return op.data.nbytes;
 }
 
 /*
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 5bfa36e95f35..3e635430bfde 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1431,13 +1431,6 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
                        goto write_err;
                *retlen += written;
                i += written;
-               if (written != page_remain) {
-                       dev_err(nor->dev,
-                               "While writing %zu bytes written %zd bytes\n",
-                               page_remain, written);
-                       ret = -EIO;
-                       goto write_err;
-               }
        }
 
 write_err:

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

end of thread, other threads:[~2018-06-11  7:36 UTC | newest]

Thread overview: 92+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-02-05 23:21 [RFC PATCH 0/6] spi: Extend the framework to generically support memory devices Boris Brezillon
2018-02-05 23:21 ` Boris Brezillon
     [not found] ` <20180205232120.5851-1-boris.brezillon-LDxbnhwyfcJBDgjK7y7TUQ@public.gmane.org>
2018-02-05 23:21   ` [RFC PATCH 1/6] spi: Extend the core to ease integration of SPI memory controllers Boris Brezillon
2018-02-05 23:21     ` Boris Brezillon
     [not found]     ` <20180205232120.5851-2-boris.brezillon-LDxbnhwyfcJBDgjK7y7TUQ@public.gmane.org>
2018-02-06  9:43       ` Maxime Chevallier
2018-02-06  9:43         ` Maxime Chevallier
2018-02-06 10:25         ` Boris Brezillon
2018-02-06 10:25           ` Boris Brezillon
2018-02-06 12:06         ` Mark Brown
2018-02-06 12:06           ` Mark Brown
2018-02-09 12:52       ` Miquel Raynal
2018-02-09 12:52         ` Miquel Raynal
2018-02-11 16:00         ` Boris Brezillon
2018-02-11 16:00           ` Boris Brezillon
2018-02-12 11:50       ` Vignesh R
2018-02-12 11:50         ` Vignesh R
     [not found]         ` <40a44152-e62c-d57e-7646-7699301c29cc-l0cyMroinI0@public.gmane.org>
2018-02-12 12:28           ` Boris Brezillon
2018-02-12 12:28             ` Boris Brezillon
2018-02-19 13:53     ` Mark Brown
2018-02-19 14:20       ` Boris Brezillon
2018-02-19 14:00     ` Mark Brown
2018-02-19 14:32       ` Boris Brezillon
2018-02-28  7:51     ` Peter Pan
2018-02-28  7:51       ` Peter Pan
2018-02-28 12:25       ` Boris Brezillon
2018-02-28 12:25         ` Boris Brezillon
2018-03-04 21:15     ` Cyrille Pitchen
2018-03-04 21:15       ` Cyrille Pitchen
2018-03-05  9:00       ` Boris Brezillon
2018-03-05  9:00         ` Boris Brezillon
2018-03-05 13:01         ` Cyrille Pitchen
2018-03-05 13:01           ` Cyrille Pitchen
2018-03-05 13:47           ` Boris Brezillon
2018-03-05 13:47             ` Boris Brezillon
2018-03-08 14:07             ` Boris Brezillon
2018-03-08 14:07               ` Boris Brezillon
2018-02-05 23:21   ` [RFC PATCH 2/6] spi: bcm-qspi: Implement the spi_mem interface Boris Brezillon
2018-02-05 23:21     ` Boris Brezillon
2018-02-05 23:21   ` [RFC PATCH 3/6] spi: bcm53xx: " Boris Brezillon
2018-02-05 23:21     ` Boris Brezillon
2018-02-05 23:21   ` [RFC PATCH 4/6] spi: ti-qspi: " Boris Brezillon
2018-02-05 23:21     ` Boris Brezillon
     [not found]     ` <20180205232120.5851-5-boris.brezillon-LDxbnhwyfcJBDgjK7y7TUQ@public.gmane.org>
2018-02-11 15:17       ` Miquel Raynal
2018-02-11 15:17         ` Miquel Raynal
2018-02-11 17:18         ` Boris Brezillon
2018-02-11 17:18           ` Boris Brezillon
2018-02-12  7:54           ` Miquel Raynal
2018-02-12  7:54             ` Miquel Raynal
2018-02-12 11:43       ` Vignesh R
2018-02-12 11:43         ` Vignesh R
     [not found]         ` <6a9eaaaf-20a6-b332-03d0-9d16e24d0b3d-l0cyMroinI0@public.gmane.org>
2018-02-12 12:31           ` Boris Brezillon
2018-02-12 12:31             ` Boris Brezillon
2018-02-12 16:00             ` Vignesh R
2018-02-12 16:00               ` Vignesh R
     [not found]               ` <67e61203-a2e9-853c-6cda-7226499611c2-l0cyMroinI0@public.gmane.org>
2018-02-12 16:08                 ` Boris Brezillon
2018-02-12 16:08                   ` Boris Brezillon
2018-02-14 16:25                   ` Vignesh R
2018-02-14 16:25                     ` Vignesh R
     [not found]                     ` <0944fefa-6ef8-a93a-dad6-660044b8ec8e-l0cyMroinI0@public.gmane.org>
2018-02-14 19:09                       ` Boris Brezillon
2018-02-14 19:09                         ` Boris Brezillon
2018-02-14 20:44                         ` Schrempf Frieder
2018-02-14 20:44                           ` Schrempf Frieder
     [not found]                           ` <561c779b-28b1-ac8a-6b27-46b5ac59344b-wPoT/lNZgHizQB+pC5nmwQ@public.gmane.org>
2018-02-14 21:00                             ` Boris Brezillon
2018-02-14 21:00                               ` Boris Brezillon
2018-02-15 16:38                               ` Schrempf Frieder
2018-02-15 16:38                                 ` Schrempf Frieder
2018-02-17 11:01                         ` Vignesh R
2018-02-17 11:01                           ` Vignesh R
     [not found]                           ` <55878296-f1c9-434b-3c7e-e2f03f5824a9-l0cyMroinI0@public.gmane.org>
2018-02-17 21:52                             ` Boris Brezillon
2018-02-17 21:52                               ` Boris Brezillon
2018-02-16 10:25       ` Boris Brezillon
2018-02-16 10:25         ` Boris Brezillon
2018-02-05 23:21   ` [RFC PATCH 5/6] mtd: spi-nor: Use the spi_mem_xx() API Boris Brezillon
2018-02-05 23:21     ` Boris Brezillon
     [not found]     ` <20180205232120.5851-6-boris.brezillon-LDxbnhwyfcJBDgjK7y7TUQ@public.gmane.org>
2018-02-12 11:44       ` Vignesh R
2018-02-12 11:44         ` Vignesh R
     [not found]         ` <933bd372-8b75-183f-0b03-563cabbbcc68-l0cyMroinI0@public.gmane.org>
2018-02-12 12:32           ` Boris Brezillon
2018-02-12 12:32             ` Boris Brezillon
2018-06-11  6:25     ` Yogesh Narayan Gaur
2018-06-11  6:25       ` Yogesh Narayan Gaur
2018-06-11  7:35       ` Boris Brezillon
2018-06-11  7:35         ` Boris Brezillon
2018-02-05 23:21   ` [RFC PATCH 6/6] spi: Get rid of the spi_flash_read() API Boris Brezillon
2018-02-05 23:21     ` Boris Brezillon
     [not found]     ` <20180205232120.5851-7-boris.brezillon-LDxbnhwyfcJBDgjK7y7TUQ@public.gmane.org>
2018-02-16 10:21       ` Vignesh R
2018-02-16 10:21         ` Vignesh R
     [not found]         ` <674d7b22-a3ac-e812-04db-aa0acb1671b0-l0cyMroinI0@public.gmane.org>
2018-02-16 10:24           ` Boris Brezillon
2018-02-16 10:24             ` Boris Brezillon
2018-02-19 16:25 ` [RFC PATCH 0/6] spi: Extend the framework to generically support memory devices Mark Brown
2018-02-19 16:51   ` Boris Brezillon
2018-03-04 21:40   ` Cyrille Pitchen
2018-03-04 21:40     ` Cyrille Pitchen

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.