Linux-SPI Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support
@ 2020-10-07 23:54 Serge Semin
  2020-10-07 23:54 ` [PATCH v4 01/21] spi: dw: Use an explicit set_cs assignment Serge Semin
                   ` (21 more replies)
  0 siblings, 22 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:54 UTC (permalink / raw)
  To: Mark Brown
  Cc: Serge Semin, Serge Semin, Alexey Malahov, Ramil Zaripov,
	Pavel Parkhomenko, Andy Shevchenko, Andy Shevchenko,
	Lars Povlsen, wuxu . wu, Feng Tang, Rob Herring, linux-spi,
	devicetree, linux-kernel

Originally I intended to merge a dedicated Baikal-T1 System Boot SPI
Controller driver into the kernel and leave the DW APB SSI driver
untouched. But after a long discussion (see the link at the bottom of the
letter) Mark and Andy persuaded me to integrate what we developed there
into the DW APB SSI core driver to be useful for another controllers,
which may have got the same peculiarities/problems as ours:
- No IRQ.
- No DMA.
- No GPIO CS, so a native CS is utilized.
- small Tx/Rx FIFO depth.
- Automatic CS assertion/de-assertion.
- Slow system bus.
All of them have been fixed in the framework of this patchset in some
extent at least for the SPI memory operations. As I expected it wasn't
that easy and the integration took that many patches as you can see from
the subject. Though some of them are mere cleanups or weakly related with
the subject fixes, but we just couldn't leave the code as is at some
places since we were working with the DW APB SSI driver anyway. Here is
what we did to fix the original DW APB SSI driver, to make it less messy.

First two patches are just cleanups to simplify the DW APB SSI device
initialization a bit. We suggest to discard the IRQ threshold macro as
unused and use a ternary operator to initialize the set_cs callback
instead of assigning-and-updating it.

Then we've discovered that the n_bytes field of the driver private data is
used by the DW APB SSI IRQ handler, which requires it to be initialized
before the SMP memory barrier and to be visible from another CPUs. Speaking
about the SMP memory barrier. Having one right after the shared resources
initialization is enough and there is no point in using the spin-lock to
protect the Tx/Rx buffer pointers. The protection functionality is
redundant there by the driver design. (Though I have a doubt whether the
SMP memory barrier is also required there because the normal IO-methods
like readl/writel implies a full memory barrier. So any memory operations
performed before them are supposed to be seen by devices and another CPUs.
See the patch log for details of my concern.)

Thirdly we've found out that there is some confusion in the IRQs
masking/unmasking/clearing in the SPI-transfer procedure. Multiple interrupts
are unmasked on the SPI-transfer initialization, but just TXEI is only
masked back on completion. Similarly IRQ status isn't cleared on the
controller reset, which actually makes the reset being not full and errors
prone in the controller probe procedure.

Another very important optimization is using the IO-relaxed accessors in
the dw_read_io_reg()/dw_write_io_reg() methods. Since the Tx/Rx FIFO data
registers are the most frequently accessible controller resource, using
relaxed accessors there will significantly improve the data read/write
performance. At least on Baikal-T1 SoC such modification opens up a way to
have the DW APB SSI controller working with higher SPI bus speeds, than
without it.

Fifthly we've made an effort to cleanup the code using the SPI-device
private data - chip_data. We suggest to remove the chip type from there
since it isn't used and isn't implemented right anyway. Then instead of
having a bus speed, clock divider, transfer mode preserved there, and
recalculating the CR0 fields of the SPI-device-specific phase, polarity
and frame format each time the SPI transfer is requested, we can save it
in the chip_data instance. By doing so we'll make that structure finally
used as it was supposed to by design (see the spi-fsl-dspi.c, spi-pl022.c,
spi-pxa2xx.c drivers for examples).

Sixthly instead of having the SPI-transfer specific CR0-update callback,
we suggest to implement the DW APB SSI controller capabilities approach.
By doing so we can now inject the vendor-specific peculiarities in
different parts of the DW APB SSI core driver (which is required to
implement both SPI-transfers and the SPI memory operations). This will
also make the code less confusing like defining a callback in the core
driver, setting it up in the glue layer, then calling it from the core
driver again. Seeing the small capabilities implementation embedded
in-situ is more readable than tracking the callbacks assignments. This
will concern the CS-override, Keembay master setup, DW SSI-specific CR0
registers layout capabilities.

Seventhly since there are going to be two types of the transfers
implemented in the DW APB SSI core driver, we need a common method to set
the controller configuration like, Tx/Rx-mode, bus speed, data frame size
and number of data frames to read in case of the memory operations. So we
just detached the corresponding code from the SPI-transfer-one method and
made it to be a part of the new dw_spi_update_config() function, which is
former update_cr0(). Note that the new method will be also useful for the
glue drivers, which due to the hardware design need to create their own
memory operations (for instance, for the dirmap-operations provided in the
Baikal-T System Boot SPI controller driver).

Eighthly it is the data IO procedure and IRQ-based SPI-transfer
implementation refactoring. The former one will look much simpler if the
buffers initial pointers and the buffers length data utilized instead of
the Tx/Rx buffers start and end pointers. The later one currently lacks of
valid execution at the final stage of the SPI-transfer. So if there is no
data left to send, but there is still data which needs to be received, the
Tx FIFO Empty IRQ will constantly happen until all of the requested
inbound data is received. So we suggest to fix that by taking the Rx FIFO
Empty IRQ into account.

Ninthly it's potentially errors prone to enable the DW APB SSI interrupts
before enabling the chip. It specifically concerns a case if for some
reason the DW APB SSI IRQs handler is executed before the controller is
enabled. That will cause a part of the outbound data loss. So we suggest
to reverse the order.

Tenthly in order to be able to pre-initialize the Tx FIFO with data and
only the start the SPI memory operations we need to have any CS
de-activated. We'll fulfil that requirement by explicitly clearing the CS
on the SPI transfer completion and at the explicit controller reset.

Then seeing all the currently available and potentially being created
types of the SPI transfers need to perform the DW APB SSI controller
status register check and the errors handler procedure, we've created a
common method for all of them.

Eleventhly if before we've mostly had a series of fixups, cleanups and
refactorings, here we've finally come to the new functionality
implementation. It concerns the poll-based transfer (as Baikal-T1 System
Boot SPI controller lacks a dedicated IRQ lane connected) and the SPI
memory operations implementation. If the former feature is pretty much
straightforward (see the patch log for details), the later one is a bit
tricky. It's based on the EEPROM-read (write-then-read) and the Tx-only
modes of the DW APB SSI controller, which as performing the automatic data
read and write let's us to implement the faster IO procedure than using
the Tx-Rx-mode-based approach. Having the memory-operations implemented
that way is the best thing we can currently do to provide the errors-less
SPI transfers to SPI devices with native CS attached.

Note the approach utilized here to develop the SPI memory operations can
be also used to create the "automatic CS toggle problem"-free(ish) SPI
transfers (combine SPI-message transfers into two buffers, disable
interrupts, push-pull the combined data). But we don't provide a solution
in the framework of this patchset. It is a matter of a dedicated one,
which we currently don't intend to spend our time on.

Finally at the closure of the this patchset you'll find patches, which
provide the Baikal-T1-specific DW APB SSI controllers support. The SoC has
got three SPI controllers. Two of them are pretty much normal DW APB SSI
interfaces: with IRQ, DMA, FIFOs of 64 words depth, 4x CSs. But the third
one as being a part of the Baikal-T1 System Boot Controller has got a very
limited resources: no IRQ, no DMA, only a single native chip-select and
Tx/Rx FIFOs with just 8 words depth available. In order to provide a
transparent initial boot code execution the System Boot SPI Controller is
also utilized by an vendor-specific IP-block, which exposes an SPI flash
memory direct mapping interface. Please see the corresponding patch for
details.

Link: https://lore.kernel.org/linux-spi/20200508093621.31619-1-Sergey.Semin@baikalelectronics.ru/

[1] "LINUX KERNEL MEMORY BARRIERS", Documentation/memory-barriers.txt,
    Section "KERNEL I/O BARRIER EFFECTS"

Link: https://lore.kernel.org/linux-spi/20200920112914.26501-1-Sergey.Semin@baikalelectronics.ru
Changelog v2:
- Replace the ternary operator with the if-else statement in the set_cs
  callback setting up.
- Get back the in-code comments to the dw_spi_update_cr0() method and it'
  further derivatives.
- Discard the patches from the series as being merged in:
  [PATCH 00/10] spi: spi-dw: Remove extraneous locking
  [PATCH 00/09] spi: dw: Add KeemBay Master capability
  [PATCH 00/08] spi: dw: Convert CS-override to DW SPI capabilities
  [PATCH 00/07] spi: dw: Discard DW SSI chip type storages
  [PATCH 00/06] spi: dw: Use relaxed IO-methods to access FIFOs
  [PATCH 00/05] spi: dw: Disable all IRQs when controller is unused
  [PATCH 00/04] spi: dw: Clear IRQ status on DW SPI controller reset
  [PATCH 00/03] spi: dw: Initialize n_bytes before the memory barrier
  [PATCH 00/01] spi: dw: Discard IRQ threshold macro

Link: https://lore.kernel.org/linux-spi/20200930185545.29959-1-Sergey.Semin@baikalelectronics.ru
Changelog v3:
- Remove dw_spi_update_cr0() callback assignment from the DW APB SSI PCI
  glue-driver.

Link: https://lore.kernel.org/linux-spi/20201001222829.15977-1-Sergey.Semin@baikalelectronics.ru
Changelog v4:
- Rename dw_spi_get_cr0() to dw_spi_prepare_cr0().

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
Cc: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
Cc: Andy Shevchenko <andy.shevchenko@gmail.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Lars Povlsen <lars.povlsen@microchip.com>
Cc: wuxu.wu <wuxu.wu@huawei.com>
Cc: Feng Tang <feng.tang@intel.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-spi@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org

Serge Semin (21):
  spi: dw: Use an explicit set_cs assignment
  spi: dw: Add DWC SSI capability
  spi: dw: Detach SPI device specific CR0 config method
  spi: dw: Update SPI bus speed in a config function
  spi: dw: Simplify the SPI bus speed config procedure
  spi: dw: Update Rx sample delay in the config function
  spi: dw: Add DW SPI controller config structure
  spi: dw: Refactor data IO procedure
  spi: dw: Refactor IRQ-based SPI transfer procedure
  spi: dw: Perform IRQ setup in a dedicated function
  spi: dw: Unmask IRQs after enabling the chip
  spi: dw: Discard chip enabling on DMA setup error
  spi: dw: De-assert chip-select on reset
  spi: dw: Explicitly de-assert CS on SPI transfer completion
  spi: dw: Move num-of retries parameter to the header file
  spi: dw: Add generic DW SSI status-check method
  spi: dw: Add memory operations support
  spi: dw: Introduce max mem-ops SPI bus frequency setting
  spi: dw: Add poll-based SPI transfers support
  dt-bindings: spi: dw: Add Baikal-T1 SPI Controllers
  spi: dw: Add Baikal-T1 SPI Controller glue driver

 .../bindings/spi/snps,dw-apb-ssi.yaml         |  33 +-
 drivers/spi/Kconfig                           |  29 +
 drivers/spi/Makefile                          |   1 +
 drivers/spi/spi-dw-bt1.c                      | 339 +++++++++
 drivers/spi/spi-dw-core.c                     | 641 ++++++++++++++----
 drivers/spi/spi-dw-dma.c                      |  16 +-
 drivers/spi/spi-dw-mmio.c                     |  20 +-
 drivers/spi/spi-dw-pci.c                      |   6 -
 drivers/spi/spi-dw.h                          |  48 +-
 9 files changed, 957 insertions(+), 176 deletions(-)
 create mode 100644 drivers/spi/spi-dw-bt1.c

-- 
2.27.0


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

* [PATCH v4 01/21] spi: dw: Use an explicit set_cs assignment
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
@ 2020-10-07 23:54 ` Serge Semin
  2020-10-07 23:54 ` [PATCH v4 02/21] spi: dw: Add DWC SSI capability Serge Semin
                   ` (20 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:54 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

Simplify the dw_spi_add_host() method a bit by replacing the currently
implemented default set_cs callback setting up and later having it
overwritten by a custom function with direct if-else-based callback
assignment.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>

---

Changelog v2:
- Replace the ternary operator with the if-else statement.
---
 drivers/spi/spi-dw-core.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index d8e92f53e2bc..3a7fdca8d335 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -477,7 +477,10 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
 	master->num_chipselect = dws->num_cs;
 	master->setup = dw_spi_setup;
 	master->cleanup = dw_spi_cleanup;
-	master->set_cs = dw_spi_set_cs;
+	if (dws->set_cs)
+		master->set_cs = dws->set_cs;
+	else
+		master->set_cs = dw_spi_set_cs;
 	master->transfer_one = dw_spi_transfer_one;
 	master->handle_err = dw_spi_handle_err;
 	master->max_speed_hz = dws->max_freq;
@@ -486,9 +489,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
 	master->flags = SPI_MASTER_GPIO_SS;
 	master->auto_runtime_pm = true;
 
-	if (dws->set_cs)
-		master->set_cs = dws->set_cs;
-
 	/* Get default rx sample delay */
 	device_property_read_u32(dev, "rx-sample-delay-ns",
 				 &dws->def_rx_sample_dly_ns);
-- 
2.27.0


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

* [PATCH v4 02/21] spi: dw: Add DWC SSI capability
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
  2020-10-07 23:54 ` [PATCH v4 01/21] spi: dw: Use an explicit set_cs assignment Serge Semin
@ 2020-10-07 23:54 ` Serge Semin
  2020-10-07 23:54 ` [PATCH v4 03/21] spi: dw: Detach SPI device specific CR0 config method Serge Semin
                   ` (19 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:54 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

Currently DWC SSI core is supported by means of setting up the
core-specific update_cr0() callback. It isn't suitable for multiple
reasons. First of all having exported several methods doing the same thing
but for different chips makes the code harder to maintain. Secondly the
spi-dw-core driver exports the methods, then the spi-dw-mmio driver sets
the private data callback with one of them so to be called by the core
driver again. That makes the code logic too complicated. Thirdly using
callbacks for just updating the CR0 register is problematic, since in case
if the register needed to be updated from different parts of the code,
we'd have to create another callback (for instance the SPI device-specific
parameters don't need to be calculated each time the SPI transfer is
submitted, so it's better to pre-calculate the CR0 data at the SPI-device
setup stage).

So keeping all the above in mind let's discard the update_cr0() callbacks,
define a generic and static dw_spi_update_cr0() method and create the
DW_SPI_CAP_DWC_SSI capability, which when enabled would activate the
alternative CR0 register layout.

While at it add the comments to the code path of the normal DW APB SSI
controller setup to make the dw_spi_update_cr0() method looking coherent.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>

---

Changelog v2:
- Get back the in-code comments to the dw_spi_update_cr0() method and it'
  further derivatives.

Changelog v3:
- Remove dw_spi_update_cr0() callback assignment from the DW APB SSI PCI
  glue-driver.
---
 drivers/spi/spi-dw-core.c | 80 ++++++++++++++++++---------------------
 drivers/spi/spi-dw-mmio.c | 20 +---------
 drivers/spi/spi-dw-pci.c  |  6 ---
 drivers/spi/spi-dw.h      |  9 +----
 4 files changed, 40 insertions(+), 75 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 3a7fdca8d335..be16fdaf7ce0 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -228,60 +228,56 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
 	return dws->transfer_handler(dws);
 }
 
-/* Configure CTRLR0 for DW_apb_ssi */
-u32 dw_spi_update_cr0(struct spi_controller *master, struct spi_device *spi,
-		      struct spi_transfer *transfer)
+static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi,
+			      struct spi_transfer *transfer)
 {
 	struct chip_data *chip = spi_get_ctldata(spi);
 	u32 cr0;
 
-	/* Default SPI mode is SCPOL = 0, SCPH = 0 */
-	cr0 = (transfer->bits_per_word - 1)
-		| (SSI_MOTO_SPI << SPI_FRF_OFFSET)
-		| ((((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET) |
-		   (((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET) |
-		   (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET))
-		| (chip->tmode << SPI_TMOD_OFFSET);
+	/* CTRLR0[ 4/3: 0] Data Frame Size */
+	cr0 = (transfer->bits_per_word - 1);
 
-	return cr0;
-}
-EXPORT_SYMBOL_GPL(dw_spi_update_cr0);
+	if (!(dws->caps & DW_SPI_CAP_DWC_SSI)) {
+		/* CTRLR0[ 5: 4] Frame Format */
+		cr0 |= SSI_MOTO_SPI << SPI_FRF_OFFSET;
 
-/* Configure CTRLR0 for DWC_ssi */
-u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master,
-			     struct spi_device *spi,
-			     struct spi_transfer *transfer)
-{
-	struct dw_spi *dws = spi_controller_get_devdata(master);
-	struct chip_data *chip = spi_get_ctldata(spi);
-	u32 cr0;
+		/*
+		 * SPI mode (SCPOL|SCPH)
+		 * CTRLR0[ 6] Serial Clock Phase
+		 * CTRLR0[ 7] Serial Clock Polarity
+		 */
+		cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET;
+		cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET;
 
-	/* CTRLR0[ 4: 0] Data Frame Size */
-	cr0 = (transfer->bits_per_word - 1);
+		/* CTRLR0[11] Shift Register Loop */
+		cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET;
 
-	/* CTRLR0[ 7: 6] Frame Format */
-	cr0 |= SSI_MOTO_SPI << DWC_SSI_CTRLR0_FRF_OFFSET;
+		/* CTRLR0[ 9:8] Transfer Mode */
+		cr0 |= chip->tmode << SPI_TMOD_OFFSET;
+	} else {
+		/* CTRLR0[ 7: 6] Frame Format */
+		cr0 |= SSI_MOTO_SPI << DWC_SSI_CTRLR0_FRF_OFFSET;
 
-	/*
-	 * SPI mode (SCPOL|SCPH)
-	 * CTRLR0[ 8] Serial Clock Phase
-	 * CTRLR0[ 9] Serial Clock Polarity
-	 */
-	cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << DWC_SSI_CTRLR0_SCPOL_OFFSET;
-	cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << DWC_SSI_CTRLR0_SCPH_OFFSET;
+		/*
+		 * SPI mode (SCPOL|SCPH)
+		 * CTRLR0[ 8] Serial Clock Phase
+		 * CTRLR0[ 9] Serial Clock Polarity
+		 */
+		cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << DWC_SSI_CTRLR0_SCPOL_OFFSET;
+		cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << DWC_SSI_CTRLR0_SCPH_OFFSET;
 
-	/* CTRLR0[11:10] Transfer Mode */
-	cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET;
+		/* CTRLR0[13] Shift Register Loop */
+		cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << DWC_SSI_CTRLR0_SRL_OFFSET;
 
-	/* CTRLR0[13] Shift Register Loop */
-	cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << DWC_SSI_CTRLR0_SRL_OFFSET;
+		/* CTRLR0[11:10] Transfer Mode */
+		cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET;
 
-	if (dws->caps & DW_SPI_CAP_KEEMBAY_MST)
-		cr0 |= DWC_SSI_CTRLR0_KEEMBAY_MST;
+		if (dws->caps & DW_SPI_CAP_KEEMBAY_MST)
+			cr0 |= DWC_SSI_CTRLR0_KEEMBAY_MST;
+	}
 
-	return cr0;
+	dw_writel(dws, DW_SPI_CTRLR0, cr0);
 }
-EXPORT_SYMBOL_GPL(dw_spi_update_cr0_v1_01a);
 
 static int dw_spi_transfer_one(struct spi_controller *master,
 		struct spi_device *spi, struct spi_transfer *transfer)
@@ -290,7 +286,6 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 	struct chip_data *chip = spi_get_ctldata(spi);
 	u8 imask = 0;
 	u16 txlevel = 0;
-	u32 cr0;
 	int ret;
 
 	dws->dma_mapped = 0;
@@ -319,8 +314,7 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 
 	transfer->effective_speed_hz = dws->max_freq / chip->clk_div;
 
-	cr0 = dws->update_cr0(master, spi, transfer);
-	dw_writel(dws, DW_SPI_CTRLR0, cr0);
+	dw_spi_update_cr0(dws, spi, transfer);
 
 	/* Check if current transfer is a DMA transaction */
 	if (master->can_dma && master->can_dma(master, spi, transfer))
diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
index c0d351fde782..d0cc5bf4fa4e 100644
--- a/drivers/spi/spi-dw-mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -110,9 +110,6 @@ static int dw_spi_mscc_init(struct platform_device *pdev,
 	dwsmmio->dws.set_cs = dw_spi_mscc_set_cs;
 	dwsmmio->priv = dwsmscc;
 
-	/* Register hook to configure CTRLR0 */
-	dwsmmio->dws.update_cr0 = dw_spi_update_cr0;
-
 	return 0;
 }
 
@@ -188,9 +185,6 @@ static int dw_spi_mscc_sparx5_init(struct platform_device *pdev,
 	dwsmmio->dws.set_cs = dw_spi_sparx5_set_cs;
 	dwsmmio->priv = dwsmscc;
 
-	/* Register hook to configure CTRLR0 */
-	dwsmmio->dws.update_cr0 = dw_spi_update_cr0;
-
 	return 0;
 }
 
@@ -199,18 +193,12 @@ static int dw_spi_alpine_init(struct platform_device *pdev,
 {
 	dwsmmio->dws.caps = DW_SPI_CAP_CS_OVERRIDE;
 
-	/* Register hook to configure CTRLR0 */
-	dwsmmio->dws.update_cr0 = dw_spi_update_cr0;
-
 	return 0;
 }
 
 static int dw_spi_dw_apb_init(struct platform_device *pdev,
 			      struct dw_spi_mmio *dwsmmio)
 {
-	/* Register hook to configure CTRLR0 */
-	dwsmmio->dws.update_cr0 = dw_spi_update_cr0;
-
 	dw_spi_dma_setup_generic(&dwsmmio->dws);
 
 	return 0;
@@ -219,8 +207,7 @@ static int dw_spi_dw_apb_init(struct platform_device *pdev,
 static int dw_spi_dwc_ssi_init(struct platform_device *pdev,
 			       struct dw_spi_mmio *dwsmmio)
 {
-	/* Register hook to configure CTRLR0 */
-	dwsmmio->dws.update_cr0 = dw_spi_update_cr0_v1_01a;
+	dwsmmio->dws.caps = DW_SPI_CAP_DWC_SSI;
 
 	dw_spi_dma_setup_generic(&dwsmmio->dws);
 
@@ -230,10 +217,7 @@ static int dw_spi_dwc_ssi_init(struct platform_device *pdev,
 static int dw_spi_keembay_init(struct platform_device *pdev,
 			       struct dw_spi_mmio *dwsmmio)
 {
-	dwsmmio->dws.caps = DW_SPI_CAP_KEEMBAY_MST;
-
-	/* Register hook to configure CTRLR0 */
-	dwsmmio->dws.update_cr0 = dw_spi_update_cr0_v1_01a;
+	dwsmmio->dws.caps = DW_SPI_CAP_KEEMBAY_MST | DW_SPI_CAP_DWC_SSI;
 
 	return 0;
 }
diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c
index 271839a8add0..8a91cd58102f 100644
--- a/drivers/spi/spi-dw-pci.c
+++ b/drivers/spi/spi-dw-pci.c
@@ -48,9 +48,6 @@ static int spi_mid_init(struct dw_spi *dws)
 
 	iounmap(clk_reg);
 
-	/* Register hook to configure CTRLR0 */
-	dws->update_cr0 = dw_spi_update_cr0;
-
 	dw_spi_dma_setup_mfld(dws);
 
 	return 0;
@@ -58,9 +55,6 @@ static int spi_mid_init(struct dw_spi *dws)
 
 static int spi_generic_init(struct dw_spi *dws)
 {
-	/* Register hook to configure CTRLR0 */
-	dws->update_cr0 = dw_spi_update_cr0;
-
 	dw_spi_dma_setup_generic(dws);
 
 	return 0;
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index da9b543322c9..c02351cf2f99 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -109,6 +109,7 @@ enum dw_ssi_type {
 /* DW SPI capabilities */
 #define DW_SPI_CAP_CS_OVERRIDE		BIT(0)
 #define DW_SPI_CAP_KEEMBAY_MST		BIT(1)
+#define DW_SPI_CAP_DWC_SSI		BIT(2)
 
 struct dw_spi;
 struct dw_spi_dma_ops {
@@ -136,8 +137,6 @@ struct dw_spi {
 	u16			bus_num;
 	u16			num_cs;		/* supported slave numbers */
 	void (*set_cs)(struct spi_device *spi, bool enable);
-	u32 (*update_cr0)(struct spi_controller *master, struct spi_device *spi,
-			  struct spi_transfer *transfer);
 
 	/* Current message transfer state info */
 	size_t			len;
@@ -254,12 +253,6 @@ extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws);
 extern void dw_spi_remove_host(struct dw_spi *dws);
 extern int dw_spi_suspend_host(struct dw_spi *dws);
 extern int dw_spi_resume_host(struct dw_spi *dws);
-extern u32 dw_spi_update_cr0(struct spi_controller *master,
-			     struct spi_device *spi,
-			     struct spi_transfer *transfer);
-extern u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master,
-				    struct spi_device *spi,
-				    struct spi_transfer *transfer);
 
 #ifdef CONFIG_SPI_DW_DMA
 
-- 
2.27.0


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

* [PATCH v4 03/21] spi: dw: Detach SPI device specific CR0 config method
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
  2020-10-07 23:54 ` [PATCH v4 01/21] spi: dw: Use an explicit set_cs assignment Serge Semin
  2020-10-07 23:54 ` [PATCH v4 02/21] spi: dw: Add DWC SSI capability Serge Semin
@ 2020-10-07 23:54 ` Serge Semin
  2020-10-07 23:54 ` [PATCH v4 04/21] spi: dw: Update SPI bus speed in a config function Serge Semin
                   ` (18 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:54 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

Indeed there is no point in detecting the SPI peripheral device parameters
and initializing the CR0 register fields each time an SPI transfer is
executed. Instead let's define a dedicated CR0 chip-data member, which
will be initialized in accordance with the SPI device settings at the
moment of setting it up.

By doing so we'll finally make the SPI device chip_data serving as it's
supposed to - to preserve the SPI device specific DW SPI configuration.
See spi-fsl-dspi.c, spi-pl022.c, spi-pxa2xx.c drivers for example of the
way the chip data is utilized.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>

---

Changelog v4:
- Rename dw_spi_get_cr0() to dw_spi_prepare_cr0().
---
 drivers/spi/spi-dw-core.c | 43 +++++++++++++++++++++++++++------------
 1 file changed, 30 insertions(+), 13 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index be16fdaf7ce0..f7a2d1919c09 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -27,6 +27,7 @@ struct chip_data {
 	u16 clk_div;		/* baud rate divider */
 	u32 speed_hz;		/* baud rate */
 
+	u32 cr0;
 	u32 rx_sample_dly;	/* RX sample delay */
 };
 
@@ -228,14 +229,9 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
 	return dws->transfer_handler(dws);
 }
 
-static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi,
-			      struct spi_transfer *transfer)
+static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi)
 {
-	struct chip_data *chip = spi_get_ctldata(spi);
-	u32 cr0;
-
-	/* CTRLR0[ 4/3: 0] Data Frame Size */
-	cr0 = (transfer->bits_per_word - 1);
+	u32 cr0 = 0;
 
 	if (!(dws->caps & DW_SPI_CAP_DWC_SSI)) {
 		/* CTRLR0[ 5: 4] Frame Format */
@@ -251,9 +247,6 @@ static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi,
 
 		/* CTRLR0[11] Shift Register Loop */
 		cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET;
-
-		/* CTRLR0[ 9:8] Transfer Mode */
-		cr0 |= chip->tmode << SPI_TMOD_OFFSET;
 	} else {
 		/* CTRLR0[ 7: 6] Frame Format */
 		cr0 |= SSI_MOTO_SPI << DWC_SSI_CTRLR0_FRF_OFFSET;
@@ -269,13 +262,29 @@ static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi,
 		/* CTRLR0[13] Shift Register Loop */
 		cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << DWC_SSI_CTRLR0_SRL_OFFSET;
 
-		/* CTRLR0[11:10] Transfer Mode */
-		cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET;
-
 		if (dws->caps & DW_SPI_CAP_KEEMBAY_MST)
 			cr0 |= DWC_SSI_CTRLR0_KEEMBAY_MST;
 	}
 
+	return cr0;
+}
+
+static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi,
+			      struct spi_transfer *transfer)
+{
+	struct chip_data *chip = spi_get_ctldata(spi);
+	u32 cr0 = chip->cr0;
+
+	/* CTRLR0[ 4/3: 0] Data Frame Size */
+	cr0 |= (transfer->bits_per_word - 1);
+
+	if (!(dws->caps & DW_SPI_CAP_DWC_SSI))
+		/* CTRLR0[ 9:8] Transfer Mode */
+		cr0 |= chip->tmode << SPI_TMOD_OFFSET;
+	else
+		/* CTRLR0[11:10] Transfer Mode */
+		cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET;
+
 	dw_writel(dws, DW_SPI_CTRLR0, cr0);
 }
 
@@ -373,6 +382,7 @@ static void dw_spi_handle_err(struct spi_controller *master,
 /* This may be called twice for each spi dev */
 static int dw_spi_setup(struct spi_device *spi)
 {
+	struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
 	struct chip_data *chip;
 
 	/* Only alloc on first setup */
@@ -396,6 +406,13 @@ static int dw_spi_setup(struct spi_device *spi)
 							dws->max_freq);
 	}
 
+	/*
+	 * Update CR0 data each time the setup callback is invoked since
+	 * the device parameters could have been changed, for instance, by
+	 * the MMC SPI driver or something else.
+	 */
+	chip->cr0 = dw_spi_prepare_cr0(dws, spi);
+
 	chip->tmode = SPI_TMOD_TR;
 
 	return 0;
-- 
2.27.0


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

* [PATCH v4 04/21] spi: dw: Update SPI bus speed in a config function
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (2 preceding siblings ...)
  2020-10-07 23:54 ` [PATCH v4 03/21] spi: dw: Detach SPI device specific CR0 config method Serge Semin
@ 2020-10-07 23:54 ` Serge Semin
  2020-10-07 23:54 ` [PATCH v4 05/21] spi: dw: Simplify the SPI bus speed config procedure Serge Semin
                   ` (17 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:54 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

The SPI bus speed update functionality will be useful in another parts of
the driver too (like to implement the SPI memory operations and from the
DW SPI glue layers). Let's move it to the update_cr0() method then and
since the later is now updating not only the CTRLR0 register alter its
prototype to have a generic function name not related to CR0.

Leave the too long line with the chip->clk_div setting as is for now,
since it's going to be changed later anyway.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index f7a2d1919c09..c82c983028f8 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -269,8 +269,8 @@ static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi)
 	return cr0;
 }
 
-static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi,
-			      struct spi_transfer *transfer)
+static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
+				 struct spi_transfer *transfer)
 {
 	struct chip_data *chip = spi_get_ctldata(spi);
 	u32 cr0 = chip->cr0;
@@ -286,6 +286,17 @@ static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi,
 		cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET;
 
 	dw_writel(dws, DW_SPI_CTRLR0, cr0);
+
+	/* Handle per transfer options for bpw and speed */
+	if (transfer->speed_hz != dws->current_freq) {
+		if (transfer->speed_hz != chip->speed_hz) {
+			/* clk_div doesn't support odd number */
+			chip->clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe;
+			chip->speed_hz = transfer->speed_hz;
+		}
+		dws->current_freq = transfer->speed_hz;
+		spi_set_clk(dws, chip->clk_div);
+	}
 }
 
 static int dw_spi_transfer_one(struct spi_controller *master,
@@ -310,21 +321,10 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 
 	spi_enable_chip(dws, 0);
 
-	/* Handle per transfer options for bpw and speed */
-	if (transfer->speed_hz != dws->current_freq) {
-		if (transfer->speed_hz != chip->speed_hz) {
-			/* clk_div doesn't support odd number */
-			chip->clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe;
-			chip->speed_hz = transfer->speed_hz;
-		}
-		dws->current_freq = transfer->speed_hz;
-		spi_set_clk(dws, chip->clk_div);
-	}
+	dw_spi_update_config(dws, spi, transfer);
 
 	transfer->effective_speed_hz = dws->max_freq / chip->clk_div;
 
-	dw_spi_update_cr0(dws, spi, transfer);
-
 	/* Check if current transfer is a DMA transaction */
 	if (master->can_dma && master->can_dma(master, spi, transfer))
 		dws->dma_mapped = master->cur_msg_mapped;
-- 
2.27.0


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

* [PATCH v4 05/21] spi: dw: Simplify the SPI bus speed config procedure
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (3 preceding siblings ...)
  2020-10-07 23:54 ` [PATCH v4 04/21] spi: dw: Update SPI bus speed in a config function Serge Semin
@ 2020-10-07 23:54 ` Serge Semin
  2020-10-07 23:54 ` [PATCH v4 06/21] spi: dw: Update Rx sample delay in the config function Serge Semin
                   ` (16 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:54 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

The code currently responsible for the SPI communication speed setting up
is a bit messy. Most likely for some historical reason the bus frequency
is saved in the peripheral chip private data. It's pointless now since the
custom communication speed is a SPI-transfer-specific thing and only if
there is no SPI transfer data specified (like during the SPI memory
operations) it can be taken from the SPI device structure. But even in the
later case there is no point in having the clock divider and the SPI bus
frequency saved in the chip data, because the controller can be used for
both SPI-transfer-based and SPI-transfer-less communications. From
software point of view keeping the current clock divider in an SPI-device
specific storage may give a small performance gain (to avoid sometimes a
round-up division), but in comparison to the total SPI transfer time it
just doesn't worth saving a few CPU cycles in comparison to the total SPI
transfer time while having the harder to read code. The only optimization,
which could worth preserving in the code is to avoid unnecessary DW SPI
controller registers update if it's possible. So to speak let's simplify
the SPI communication speed update procedure by removing the clock-related
fields from the peripheral chip data and update the DW SPI clock divider
only if it's really changed. The later change is reached by keeping the
effective SPI bus speed in the internal DW SPI private data.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index c82c983028f8..cc341080d1a4 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -24,9 +24,6 @@
 struct chip_data {
 	u8 tmode;		/* TR/TO/RO/EEPROM */
 
-	u16 clk_div;		/* baud rate divider */
-	u32 speed_hz;		/* baud rate */
-
 	u32 cr0;
 	u32 rx_sample_dly;	/* RX sample delay */
 };
@@ -274,6 +271,8 @@ static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
 {
 	struct chip_data *chip = spi_get_ctldata(spi);
 	u32 cr0 = chip->cr0;
+	u32 speed_hz;
+	u16 clk_div;
 
 	/* CTRLR0[ 4/3: 0] Data Frame Size */
 	cr0 |= (transfer->bits_per_word - 1);
@@ -287,15 +286,13 @@ static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
 
 	dw_writel(dws, DW_SPI_CTRLR0, cr0);
 
-	/* Handle per transfer options for bpw and speed */
-	if (transfer->speed_hz != dws->current_freq) {
-		if (transfer->speed_hz != chip->speed_hz) {
-			/* clk_div doesn't support odd number */
-			chip->clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe;
-			chip->speed_hz = transfer->speed_hz;
-		}
-		dws->current_freq = transfer->speed_hz;
-		spi_set_clk(dws, chip->clk_div);
+	/* Note DW APB SSI clock divider doesn't support odd numbers */
+	clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe;
+	speed_hz = dws->max_freq / clk_div;
+
+	if (dws->current_freq != speed_hz) {
+		spi_set_clk(dws, clk_div);
+		dws->current_freq = speed_hz;
 	}
 }
 
@@ -323,7 +320,7 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 
 	dw_spi_update_config(dws, spi, transfer);
 
-	transfer->effective_speed_hz = dws->max_freq / chip->clk_div;
+	transfer->effective_speed_hz = dws->current_freq;
 
 	/* Check if current transfer is a DMA transaction */
 	if (master->can_dma && master->can_dma(master, spi, transfer))
-- 
2.27.0


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

* [PATCH v4 06/21] spi: dw: Update Rx sample delay in the config function
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (4 preceding siblings ...)
  2020-10-07 23:54 ` [PATCH v4 05/21] spi: dw: Simplify the SPI bus speed config procedure Serge Semin
@ 2020-10-07 23:54 ` Serge Semin
  2020-10-07 23:54 ` [PATCH v4 07/21] spi: dw: Add DW SPI controller config structure Serge Semin
                   ` (15 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:54 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

Rx sample delay can be SPI device specific, and should be synchronously
initialized with the rest of the communication and peripheral device
related controller setups. So let's move the Rx-sample delay setup into
the DW APB SSI configuration update method.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index cc341080d1a4..12080ea2ad84 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -294,13 +294,18 @@ static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
 		spi_set_clk(dws, clk_div);
 		dws->current_freq = speed_hz;
 	}
+
+	/* Update RX sample delay if required */
+	if (dws->cur_rx_sample_dly != chip->rx_sample_dly) {
+		dw_writel(dws, DW_SPI_RX_SAMPLE_DLY, chip->rx_sample_dly);
+		dws->cur_rx_sample_dly = chip->rx_sample_dly;
+	}
 }
 
 static int dw_spi_transfer_one(struct spi_controller *master,
 		struct spi_device *spi, struct spi_transfer *transfer)
 {
 	struct dw_spi *dws = spi_controller_get_devdata(master);
-	struct chip_data *chip = spi_get_ctldata(spi);
 	u8 imask = 0;
 	u16 txlevel = 0;
 	int ret;
@@ -326,12 +331,6 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 	if (master->can_dma && master->can_dma(master, spi, transfer))
 		dws->dma_mapped = master->cur_msg_mapped;
 
-	/* Update RX sample delay if required */
-	if (dws->cur_rx_sample_dly != chip->rx_sample_dly) {
-		dw_writel(dws, DW_SPI_RX_SAMPLE_DLY, chip->rx_sample_dly);
-		dws->cur_rx_sample_dly = chip->rx_sample_dly;
-	}
-
 	/* For poll mode just disable all interrupts */
 	spi_mask_intr(dws, 0xff);
 
-- 
2.27.0


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

* [PATCH v4 07/21] spi: dw: Add DW SPI controller config structure
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (5 preceding siblings ...)
  2020-10-07 23:54 ` [PATCH v4 06/21] spi: dw: Update Rx sample delay in the config function Serge Semin
@ 2020-10-07 23:54 ` Serge Semin
  2020-10-07 23:54 ` [PATCH v4 08/21] spi: dw: Refactor data IO procedure Serge Semin
                   ` (14 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:54 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

DW APB SSI controller can be used by the two SPI core interfaces:
traditional SPI transfers and SPI memory operations. The controller needs
to be accordingly configured at runtime when the corresponding operations
are executed. In order to do that for the both interfaces from a single
function we introduce a new data wrapper for the transfer mode, data
width, number of data frames (for the automatic data transfer) and the bus
frequency. It will be used by the update_config() method to tune the DW
APB SSI up.

The update_config() method is made exported to be used not only by the DW
SPI core driver, but by the glue layer drivers too. This will be required
in a coming further commit.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 29 +++++++++++++++++------------
 drivers/spi/spi-dw.h      | 10 ++++++++++
 2 files changed, 27 insertions(+), 12 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 12080ea2ad84..01ab743bf177 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -20,10 +20,8 @@
 #include <linux/debugfs.h>
 #endif
 
-/* Slave spi_dev related */
+/* Slave spi_device related */
 struct chip_data {
-	u8 tmode;		/* TR/TO/RO/EEPROM */
-
 	u32 cr0;
 	u32 rx_sample_dly;	/* RX sample delay */
 };
@@ -266,8 +264,8 @@ static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi)
 	return cr0;
 }
 
-static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
-				 struct spi_transfer *transfer)
+void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
+			  struct dw_spi_cfg *cfg)
 {
 	struct chip_data *chip = spi_get_ctldata(spi);
 	u32 cr0 = chip->cr0;
@@ -275,19 +273,22 @@ static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
 	u16 clk_div;
 
 	/* CTRLR0[ 4/3: 0] Data Frame Size */
-	cr0 |= (transfer->bits_per_word - 1);
+	cr0 |= (cfg->dfs - 1);
 
 	if (!(dws->caps & DW_SPI_CAP_DWC_SSI))
 		/* CTRLR0[ 9:8] Transfer Mode */
-		cr0 |= chip->tmode << SPI_TMOD_OFFSET;
+		cr0 |= cfg->tmode << SPI_TMOD_OFFSET;
 	else
 		/* CTRLR0[11:10] Transfer Mode */
-		cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET;
+		cr0 |= cfg->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET;
 
 	dw_writel(dws, DW_SPI_CTRLR0, cr0);
 
+	if (cfg->tmode == SPI_TMOD_EPROMREAD || cfg->tmode == SPI_TMOD_RO)
+		dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0);
+
 	/* Note DW APB SSI clock divider doesn't support odd numbers */
-	clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe;
+	clk_div = (DIV_ROUND_UP(dws->max_freq, cfg->freq) + 1) & 0xfffe;
 	speed_hz = dws->max_freq / clk_div;
 
 	if (dws->current_freq != speed_hz) {
@@ -301,11 +302,17 @@ static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
 		dws->cur_rx_sample_dly = chip->rx_sample_dly;
 	}
 }
+EXPORT_SYMBOL_GPL(dw_spi_update_config);
 
 static int dw_spi_transfer_one(struct spi_controller *master,
 		struct spi_device *spi, struct spi_transfer *transfer)
 {
 	struct dw_spi *dws = spi_controller_get_devdata(master);
+	struct dw_spi_cfg cfg = {
+		.tmode = SPI_TMOD_TR,
+		.dfs = transfer->bits_per_word,
+		.freq = transfer->speed_hz,
+	};
 	u8 imask = 0;
 	u16 txlevel = 0;
 	int ret;
@@ -323,7 +330,7 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 
 	spi_enable_chip(dws, 0);
 
-	dw_spi_update_config(dws, spi, transfer);
+	dw_spi_update_config(dws, spi, &cfg);
 
 	transfer->effective_speed_hz = dws->current_freq;
 
@@ -409,8 +416,6 @@ static int dw_spi_setup(struct spi_device *spi)
 	 */
 	chip->cr0 = dw_spi_prepare_cr0(dws, spi);
 
-	chip->tmode = SPI_TMOD_TR;
-
 	return 0;
 }
 
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index c02351cf2f99..2a2346438564 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -111,6 +111,14 @@ enum dw_ssi_type {
 #define DW_SPI_CAP_KEEMBAY_MST		BIT(1)
 #define DW_SPI_CAP_DWC_SSI		BIT(2)
 
+/* Slave spi_transfer/spi_mem_op related */
+struct dw_spi_cfg {
+	u8 tmode;
+	u8 dfs;
+	u32 ndf;
+	u32 freq;
+};
+
 struct dw_spi;
 struct dw_spi_dma_ops {
 	int (*dma_init)(struct device *dev, struct dw_spi *dws);
@@ -249,6 +257,8 @@ static inline void spi_shutdown_chip(struct dw_spi *dws)
 }
 
 extern void dw_spi_set_cs(struct spi_device *spi, bool enable);
+extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
+				 struct dw_spi_cfg *cfg);
 extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws);
 extern void dw_spi_remove_host(struct dw_spi *dws);
 extern int dw_spi_suspend_host(struct dw_spi *dws);
-- 
2.27.0


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

* [PATCH v4 08/21] spi: dw: Refactor data IO procedure
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (6 preceding siblings ...)
  2020-10-07 23:54 ` [PATCH v4 07/21] spi: dw: Add DW SPI controller config structure Serge Semin
@ 2020-10-07 23:54 ` Serge Semin
  2020-10-07 23:54 ` [PATCH v4 09/21] spi: dw: Refactor IRQ-based SPI transfer procedure Serge Semin
                   ` (13 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:54 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

The Tx and Rx data write/read procedure can be significantly simplified by
using Tx/Rx transfer lengths instead of the end pointers. By having the
Tx/Rx data leftover lengths (in the number of transfer words) we can get
rid of all subtraction and division operations utilized here and there in
the tx_max(), rx_max(), dw_writer() and dw_reader() methods. Such
modification will not only give us the more optimized IO procedures, but
will make the data IO methods much more readable than before.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 37 +++++++++++++++++--------------------
 drivers/spi/spi-dw.h      |  5 ++---
 2 files changed, 19 insertions(+), 23 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 01ab743bf177..3bb8aa3cba2b 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -108,9 +108,8 @@ EXPORT_SYMBOL_GPL(dw_spi_set_cs);
 /* Return the max entries we can fill into tx fifo */
 static inline u32 tx_max(struct dw_spi *dws)
 {
-	u32 tx_left, tx_room, rxtx_gap;
+	u32 tx_room, rxtx_gap;
 
-	tx_left = (dws->tx_end - dws->tx) / dws->n_bytes;
 	tx_room = dws->fifo_len - dw_readl(dws, DW_SPI_TXFLR);
 
 	/*
@@ -121,18 +120,15 @@ static inline u32 tx_max(struct dw_spi *dws)
 	 * shift registers. So a control from sw point of
 	 * view is taken.
 	 */
-	rxtx_gap =  ((dws->rx_end - dws->rx) - (dws->tx_end - dws->tx))
-			/ dws->n_bytes;
+	rxtx_gap = dws->fifo_len - (dws->rx_len - dws->tx_len);
 
-	return min3(tx_left, tx_room, (u32) (dws->fifo_len - rxtx_gap));
+	return min3((u32)dws->tx_len, tx_room, rxtx_gap);
 }
 
 /* Return the max entries we should read out of rx fifo */
 static inline u32 rx_max(struct dw_spi *dws)
 {
-	u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes;
-
-	return min_t(u32, rx_left, dw_readl(dws, DW_SPI_RXFLR));
+	return min_t(u32, dws->rx_len, dw_readl(dws, DW_SPI_RXFLR));
 }
 
 static void dw_writer(struct dw_spi *dws)
@@ -141,15 +137,16 @@ static void dw_writer(struct dw_spi *dws)
 	u16 txw = 0;
 
 	while (max--) {
-		/* Set the tx word if the transfer's original "tx" is not null */
-		if (dws->tx_end - dws->len) {
+		if (dws->tx) {
 			if (dws->n_bytes == 1)
 				txw = *(u8 *)(dws->tx);
 			else
 				txw = *(u16 *)(dws->tx);
+
+			dws->tx += dws->n_bytes;
 		}
 		dw_write_io_reg(dws, DW_SPI_DR, txw);
-		dws->tx += dws->n_bytes;
+		--dws->tx_len;
 	}
 }
 
@@ -160,14 +157,15 @@ static void dw_reader(struct dw_spi *dws)
 
 	while (max--) {
 		rxw = dw_read_io_reg(dws, DW_SPI_DR);
-		/* Care rx only if the transfer's original "rx" is not null */
-		if (dws->rx_end - dws->len) {
+		if (dws->rx) {
 			if (dws->n_bytes == 1)
 				*(u8 *)(dws->rx) = rxw;
 			else
 				*(u16 *)(dws->rx) = rxw;
+
+			dws->rx += dws->n_bytes;
 		}
-		dws->rx += dws->n_bytes;
+		--dws->rx_len;
 	}
 }
 
@@ -192,7 +190,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
 	}
 
 	dw_reader(dws);
-	if (dws->rx_end == dws->rx) {
+	if (!dws->rx_len) {
 		spi_mask_intr(dws, 0xff);
 		spi_finalize_current_transfer(dws->master);
 		return IRQ_HANDLED;
@@ -320,12 +318,11 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 	dws->dma_mapped = 0;
 	dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE);
 	dws->tx = (void *)transfer->tx_buf;
-	dws->tx_end = dws->tx + transfer->len;
+	dws->tx_len = transfer->len / dws->n_bytes;
 	dws->rx = transfer->rx_buf;
-	dws->rx_end = dws->rx + transfer->len;
-	dws->len = transfer->len;
+	dws->rx_len = dws->tx_len;
 
-	/* Ensure dw->rx and dw->rx_end are visible */
+	/* Ensure the data above is visible for all CPUs */
 	smp_mb();
 
 	spi_enable_chip(dws, 0);
@@ -352,7 +349,7 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 			return ret;
 		}
 	} else {
-		txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes);
+		txlevel = min_t(u16, dws->fifo_len / 2, dws->tx_len);
 		dw_writel(dws, DW_SPI_TXFTLR, txlevel);
 
 		/* Set the interrupt mask */
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 2a2346438564..cfc9f63acde4 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -147,11 +147,10 @@ struct dw_spi {
 	void (*set_cs)(struct spi_device *spi, bool enable);
 
 	/* Current message transfer state info */
-	size_t			len;
 	void			*tx;
-	void			*tx_end;
+	unsigned int		tx_len;
 	void			*rx;
-	void			*rx_end;
+	unsigned int		rx_len;
 	int			dma_mapped;
 	u8			n_bytes;	/* current is a 1/2 bytes op */
 	irqreturn_t		(*transfer_handler)(struct dw_spi *dws);
-- 
2.27.0


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

* [PATCH v4 09/21] spi: dw: Refactor IRQ-based SPI transfer procedure
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (7 preceding siblings ...)
  2020-10-07 23:54 ` [PATCH v4 08/21] spi: dw: Refactor data IO procedure Serge Semin
@ 2020-10-07 23:54 ` Serge Semin
  2020-10-07 23:54 ` [PATCH v4 10/21] spi: dw: Perform IRQ setup in a dedicated function Serge Semin
                   ` (12 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:54 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

Current IRQ-based SPI transfer execution procedure doesn't work well at
the final stage of the execution. If all the Tx data is sent out (written
to the Tx FIFO) but there is some data left to receive, the Tx FIFO Empty
IRQ will constantly happen until all of the requested inbound data is
received. Though for a short period of time, but it will make the system
less responsive. In order to fix that let's refactor the SPI transfer
execution procedure by taking the Rx FIFO Full IRQ into account. We'll read
and write SPI transfer data each time the IRQ happens as before. If all
the outbound data is sent out, we'll disable the Tx FIFO Empty IRQ. If
there is still some data to receive, we'll adjust the Rx FIFO Threshold
level, so the next IRQ would be raised at the moment of all incoming data
being available in the Rx FIFO.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 33 ++++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 3bb8aa3cba2b..4a45610c85f1 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -189,17 +189,30 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
 		return IRQ_HANDLED;
 	}
 
+	/*
+	 * Read data from the Rx FIFO every time we've got a chance executing
+	 * this method. If there is nothing left to receive, terminate the
+	 * procedure. Otherwise adjust the Rx FIFO Threshold level if it's a
+	 * final stage of the transfer. By doing so we'll get the next IRQ
+	 * right when the leftover incoming data is received.
+	 */
 	dw_reader(dws);
 	if (!dws->rx_len) {
 		spi_mask_intr(dws, 0xff);
 		spi_finalize_current_transfer(dws->master);
-		return IRQ_HANDLED;
+	} else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) {
+		dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1);
 	}
+
+	/*
+	 * Send data out if Tx FIFO Empty IRQ is received. The IRQ will be
+	 * disabled after the data transmission is finished so not to
+	 * have the TXE IRQ flood at the final stage of the transfer.
+	 */
 	if (irq_status & SPI_INT_TXEI) {
-		spi_mask_intr(dws, SPI_INT_TXEI);
 		dw_writer(dws);
-		/* Enable TX irq always, it will be disabled when RX finished */
-		spi_umask_intr(dws, SPI_INT_TXEI);
+		if (!dws->tx_len)
+			spi_mask_intr(dws, SPI_INT_TXEI);
 	}
 
 	return IRQ_HANDLED;
@@ -338,10 +351,6 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 	/* For poll mode just disable all interrupts */
 	spi_mask_intr(dws, 0xff);
 
-	/*
-	 * Interrupt mode
-	 * we only need set the TXEI IRQ, as TX/RX always happen syncronizely
-	 */
 	if (dws->dma_mapped) {
 		ret = dws->dma_ops->dma_setup(dws, transfer);
 		if (ret < 0) {
@@ -349,12 +358,18 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 			return ret;
 		}
 	} else {
+		/*
+		 * Originally Tx and Rx data lengths match. Rx FIFO Threshold level
+		 * will be adjusted at the final stage of the IRQ-based SPI transfer
+		 * execution so not to lose the leftover of the incoming data.
+		 */
 		txlevel = min_t(u16, dws->fifo_len / 2, dws->tx_len);
 		dw_writel(dws, DW_SPI_TXFTLR, txlevel);
+		dw_writel(dws, DW_SPI_RXFTLR, txlevel - 1);
 
 		/* Set the interrupt mask */
 		imask |= SPI_INT_TXEI | SPI_INT_TXOI |
-			 SPI_INT_RXUI | SPI_INT_RXOI;
+			 SPI_INT_RXUI | SPI_INT_RXOI | SPI_INT_RXFI;
 		spi_umask_intr(dws, imask);
 
 		dws->transfer_handler = interrupt_transfer;
-- 
2.27.0


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

* [PATCH v4 10/21] spi: dw: Perform IRQ setup in a dedicated function
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (8 preceding siblings ...)
  2020-10-07 23:54 ` [PATCH v4 09/21] spi: dw: Refactor IRQ-based SPI transfer procedure Serge Semin
@ 2020-10-07 23:54 ` Serge Semin
  2020-10-07 23:55 ` [PATCH v4 11/21] spi: dw: Unmask IRQs after enabling the chip Serge Semin
                   ` (11 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:54 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

In order to make the transfer_one() callback method more readable and
for unification with the DMA-based transfer, let's detach the IRQ setup
procedure into a dedicated function. While at it rename the IRQ-based
transfer handler function to be dw_spi-prefixe and looking more like the
DMA-related one.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 41 ++++++++++++++++++++++-----------------
 1 file changed, 23 insertions(+), 18 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 4a45610c85f1..e7ffcfff6594 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -178,7 +178,7 @@ static void int_error_stop(struct dw_spi *dws, const char *msg)
 	spi_finalize_current_transfer(dws->master);
 }
 
-static irqreturn_t interrupt_transfer(struct dw_spi *dws)
+static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
 {
 	u16 irq_status = dw_readl(dws, DW_SPI_ISR);
 
@@ -315,6 +315,27 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
 }
 EXPORT_SYMBOL_GPL(dw_spi_update_config);
 
+static void dw_spi_irq_setup(struct dw_spi *dws)
+{
+	u16 level;
+	u8 imask;
+
+	/*
+	 * Originally Tx and Rx data lengths match. Rx FIFO Threshold level
+	 * will be adjusted at the final stage of the IRQ-based SPI transfer
+	 * execution so not to lose the leftover of the incoming data.
+	 */
+	level = min_t(u16, dws->fifo_len / 2, dws->tx_len);
+	dw_writel(dws, DW_SPI_TXFTLR, level);
+	dw_writel(dws, DW_SPI_RXFTLR, level - 1);
+
+	imask = SPI_INT_TXEI | SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI |
+		SPI_INT_RXFI;
+	spi_umask_intr(dws, imask);
+
+	dws->transfer_handler = dw_spi_transfer_handler;
+}
+
 static int dw_spi_transfer_one(struct spi_controller *master,
 		struct spi_device *spi, struct spi_transfer *transfer)
 {
@@ -324,8 +345,6 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 		.dfs = transfer->bits_per_word,
 		.freq = transfer->speed_hz,
 	};
-	u8 imask = 0;
-	u16 txlevel = 0;
 	int ret;
 
 	dws->dma_mapped = 0;
@@ -358,21 +377,7 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 			return ret;
 		}
 	} else {
-		/*
-		 * Originally Tx and Rx data lengths match. Rx FIFO Threshold level
-		 * will be adjusted at the final stage of the IRQ-based SPI transfer
-		 * execution so not to lose the leftover of the incoming data.
-		 */
-		txlevel = min_t(u16, dws->fifo_len / 2, dws->tx_len);
-		dw_writel(dws, DW_SPI_TXFTLR, txlevel);
-		dw_writel(dws, DW_SPI_RXFTLR, txlevel - 1);
-
-		/* Set the interrupt mask */
-		imask |= SPI_INT_TXEI | SPI_INT_TXOI |
-			 SPI_INT_RXUI | SPI_INT_RXOI | SPI_INT_RXFI;
-		spi_umask_intr(dws, imask);
-
-		dws->transfer_handler = interrupt_transfer;
+		dw_spi_irq_setup(dws);
 	}
 
 	spi_enable_chip(dws, 1);
-- 
2.27.0


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

* [PATCH v4 11/21] spi: dw: Unmask IRQs after enabling the chip
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (9 preceding siblings ...)
  2020-10-07 23:54 ` [PATCH v4 10/21] spi: dw: Perform IRQ setup in a dedicated function Serge Semin
@ 2020-10-07 23:55 ` Serge Semin
  2020-10-07 23:55 ` [PATCH v4 12/21] spi: dw: Discard chip enabling on DMA setup error Serge Semin
                   ` (10 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:55 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

It's theoretically erroneous to enable IRQ before the chip is turned on.
If IRQ handler gets executed before the chip is enabled, then any data
written to the Tx FIFO will be just ignored.

I say "theoretically" because we haven't noticed any problem with that,
but let's fix it anyway just in case...

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index e7ffcfff6594..89e5428c8de6 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -376,8 +376,6 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 			spi_enable_chip(dws, 1);
 			return ret;
 		}
-	} else {
-		dw_spi_irq_setup(dws);
 	}
 
 	spi_enable_chip(dws, 1);
@@ -385,6 +383,8 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 	if (dws->dma_mapped)
 		return dws->dma_ops->dma_transfer(dws, transfer);
 
+	dw_spi_irq_setup(dws);
+
 	return 1;
 }
 
-- 
2.27.0


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

* [PATCH v4 12/21] spi: dw: Discard chip enabling on DMA setup error
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (10 preceding siblings ...)
  2020-10-07 23:55 ` [PATCH v4 11/21] spi: dw: Unmask IRQs after enabling the chip Serge Semin
@ 2020-10-07 23:55 ` Serge Semin
  2020-10-07 23:55 ` [PATCH v4 13/21] spi: dw: De-assert chip-select on reset Serge Semin
                   ` (9 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:55 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

It's pointless to enable the chip back if the DMA setup procedure fails,
since we'll disable it on the next transfer anyway. For the same reason We
don't do that in case of a failure detected in any other methods called
from the transfer_one() method.

While at it consider any non-zero value returned from the dma_setup
callback to be erroneous as it's supposed to be in the kernel.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 89e5428c8de6..ac87ff6d8be4 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -372,10 +372,8 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 
 	if (dws->dma_mapped) {
 		ret = dws->dma_ops->dma_setup(dws, transfer);
-		if (ret < 0) {
-			spi_enable_chip(dws, 1);
+		if (ret)
 			return ret;
-		}
 	}
 
 	spi_enable_chip(dws, 1);
-- 
2.27.0


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

* [PATCH v4 13/21] spi: dw: De-assert chip-select on reset
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (11 preceding siblings ...)
  2020-10-07 23:55 ` [PATCH v4 12/21] spi: dw: Discard chip enabling on DMA setup error Serge Semin
@ 2020-10-07 23:55 ` Serge Semin
  2020-10-07 23:55 ` [PATCH v4 14/21] spi: dw: Explicitly de-assert CS on SPI transfer completion Serge Semin
                   ` (8 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:55 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

SPI memory operations implementation will require to have the CS register
cleared before executing the operation in order not to have the
transmission automatically started prior the Tx FIFO is pre-initialized.
Let's clear the register then on explicit controller reset to fulfil the
requirements in case of an error or having the CS left set by a bootloader
or another software.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw.h | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index cfc9f63acde4..eb1d46983319 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -237,15 +237,16 @@ static inline void spi_umask_intr(struct dw_spi *dws, u32 mask)
 }
 
 /*
- * This disables the SPI controller, interrupts, clears the interrupts status,
- * and re-enable the controller back. Transmit and receive FIFO buffers are
- * cleared when the device is disabled.
+ * This disables the SPI controller, interrupts, clears the interrupts status
+ * and CS, then re-enables the controller back. Transmit and receive FIFO
+ * buffers are cleared when the device is disabled.
  */
 static inline void spi_reset_chip(struct dw_spi *dws)
 {
 	spi_enable_chip(dws, 0);
 	spi_mask_intr(dws, 0xff);
 	dw_readl(dws, DW_SPI_ICR);
+	dw_writel(dws, DW_SPI_SER, 0);
 	spi_enable_chip(dws, 1);
 }
 
-- 
2.27.0


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

* [PATCH v4 14/21] spi: dw: Explicitly de-assert CS on SPI transfer completion
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (12 preceding siblings ...)
  2020-10-07 23:55 ` [PATCH v4 13/21] spi: dw: De-assert chip-select on reset Serge Semin
@ 2020-10-07 23:55 ` Serge Semin
  2020-10-07 23:55 ` [PATCH v4 15/21] spi: dw: Move num-of retries parameter to the header file Serge Semin
                   ` (7 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:55 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

By design of the currently available native set_cs callback, the CS
de-assertion will be done only if it's required by the corresponding
controller capability. But in order to pre-fill the Tx FIFO buffer with
data during the SPI memory ops execution the SER register needs to be left
cleared before that. We'll also need a way to explicitly set and clear the
corresponding CS bit at a certain moment of the operation. Let's alter
the set_cs function then to also de-activate the CS, when it's required.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index ac87ff6d8be4..76e323db170f 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -100,7 +100,7 @@ void dw_spi_set_cs(struct spi_device *spi, bool enable)
 	 */
 	if (cs_high == enable)
 		dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select));
-	else if (dws->caps & DW_SPI_CAP_CS_OVERRIDE)
+	else
 		dw_writel(dws, DW_SPI_SER, 0);
 }
 EXPORT_SYMBOL_GPL(dw_spi_set_cs);
-- 
2.27.0


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

* [PATCH v4 15/21] spi: dw: Move num-of retries parameter to the header file
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (13 preceding siblings ...)
  2020-10-07 23:55 ` [PATCH v4 14/21] spi: dw: Explicitly de-assert CS on SPI transfer completion Serge Semin
@ 2020-10-07 23:55 ` Serge Semin
  2020-10-07 23:55 ` [PATCH v4 16/21] spi: dw: Add generic DW SSI status-check method Serge Semin
                   ` (6 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:55 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

The parameter will be needed for another wait-done method being added in
the framework of the SPI memory operation modification in a further
commit.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-dma.c | 5 ++---
 drivers/spi/spi-dw.h     | 2 ++
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c
index bb390ff67d1d..9db119dc5554 100644
--- a/drivers/spi/spi-dw-dma.c
+++ b/drivers/spi/spi-dw-dma.c
@@ -17,7 +17,6 @@
 
 #include "spi-dw.h"
 
-#define WAIT_RETRIES	5
 #define RX_BUSY		0
 #define RX_BURST_LEVEL	16
 #define TX_BUSY		1
@@ -208,7 +207,7 @@ static inline bool dw_spi_dma_tx_busy(struct dw_spi *dws)
 static int dw_spi_dma_wait_tx_done(struct dw_spi *dws,
 				   struct spi_transfer *xfer)
 {
-	int retry = WAIT_RETRIES;
+	int retry = SPI_WAIT_RETRIES;
 	struct spi_delay delay;
 	u32 nents;
 
@@ -283,7 +282,7 @@ static inline bool dw_spi_dma_rx_busy(struct dw_spi *dws)
 
 static int dw_spi_dma_wait_rx_done(struct dw_spi *dws)
 {
-	int retry = WAIT_RETRIES;
+	int retry = SPI_WAIT_RETRIES;
 	struct spi_delay delay;
 	unsigned long ns, us;
 	u32 nents;
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index eb1d46983319..946065201c9c 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -100,6 +100,8 @@
 #define SPI_DMA_RDMAE			(1 << 0)
 #define SPI_DMA_TDMAE			(1 << 1)
 
+#define SPI_WAIT_RETRIES		5
+
 enum dw_ssi_type {
 	SSI_MOTO_SPI = 0,
 	SSI_TI_SSP,
-- 
2.27.0


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

* [PATCH v4 16/21] spi: dw: Add generic DW SSI status-check method
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (14 preceding siblings ...)
  2020-10-07 23:55 ` [PATCH v4 15/21] spi: dw: Move num-of retries parameter to the header file Serge Semin
@ 2020-10-07 23:55 ` Serge Semin
  2020-10-07 23:55 ` [PATCH v4 17/21] spi: dw: Add memory operations support Serge Semin
                   ` (5 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:55 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

The DW SSI errors handling method can be generically implemented for all
types of the transfers: IRQ, DMA and poll-based ones. It will be a
function which checks the overflow/underflow error flags and resets the
controller if any of them is set. In the framework of this commit we make
use of the new method to detect the errors in the IRQ- and DMA-based SPI
transfer execution procedures.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 43 +++++++++++++++++++++++++++++++--------
 drivers/spi/spi-dw-dma.c  | 11 ++--------
 drivers/spi/spi-dw.h      |  1 +
 3 files changed, 37 insertions(+), 18 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 76e323db170f..8480da49a6a1 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -169,23 +169,48 @@ static void dw_reader(struct dw_spi *dws)
 	}
 }
 
-static void int_error_stop(struct dw_spi *dws, const char *msg)
+int dw_spi_check_status(struct dw_spi *dws, bool raw)
 {
-	spi_reset_chip(dws);
+	u32 irq_status;
+	int ret = 0;
+
+	if (raw)
+		irq_status = dw_readl(dws, DW_SPI_RISR);
+	else
+		irq_status = dw_readl(dws, DW_SPI_ISR);
+
+	if (irq_status & SPI_INT_RXOI) {
+		dev_err(&dws->master->dev, "RX FIFO overflow detected\n");
+		ret = -EIO;
+	}
+
+	if (irq_status & SPI_INT_RXUI) {
+		dev_err(&dws->master->dev, "RX FIFO underflow detected\n");
+		ret = -EIO;
+	}
 
-	dev_err(&dws->master->dev, "%s\n", msg);
-	dws->master->cur_msg->status = -EIO;
-	spi_finalize_current_transfer(dws->master);
+	if (irq_status & SPI_INT_TXOI) {
+		dev_err(&dws->master->dev, "TX FIFO overflow detected\n");
+		ret = -EIO;
+	}
+
+	/* Generically handle the erroneous situation */
+	if (ret) {
+		spi_reset_chip(dws);
+		if (dws->master->cur_msg)
+			dws->master->cur_msg->status = ret;
+	}
+
+	return ret;
 }
+EXPORT_SYMBOL_GPL(dw_spi_check_status);
 
 static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
 {
 	u16 irq_status = dw_readl(dws, DW_SPI_ISR);
 
-	/* Error handling */
-	if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) {
-		dw_readl(dws, DW_SPI_ICR);
-		int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun");
+	if (dw_spi_check_status(dws, false)) {
+		spi_finalize_current_transfer(dws->master);
 		return IRQ_HANDLED;
 	}
 
diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c
index 9db119dc5554..1969b09b4f5e 100644
--- a/drivers/spi/spi-dw-dma.c
+++ b/drivers/spi/spi-dw-dma.c
@@ -144,17 +144,10 @@ static void dw_spi_dma_exit(struct dw_spi *dws)
 
 static irqreturn_t dw_spi_dma_transfer_handler(struct dw_spi *dws)
 {
-	u16 irq_status = dw_readl(dws, DW_SPI_ISR);
+	dw_spi_check_status(dws, false);
 
-	if (!irq_status)
-		return IRQ_NONE;
-
-	dw_readl(dws, DW_SPI_ICR);
-	spi_reset_chip(dws);
-
-	dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__);
-	dws->master->cur_msg->status = -EIO;
 	complete(&dws->dma_completion);
+
 	return IRQ_HANDLED;
 }
 
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 946065201c9c..5eb98ece2f2a 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -261,6 +261,7 @@ static inline void spi_shutdown_chip(struct dw_spi *dws)
 extern void dw_spi_set_cs(struct spi_device *spi, bool enable);
 extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
 				 struct dw_spi_cfg *cfg);
+extern int dw_spi_check_status(struct dw_spi *dws, bool raw);
 extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws);
 extern void dw_spi_remove_host(struct dw_spi *dws);
 extern int dw_spi_suspend_host(struct dw_spi *dws);
-- 
2.27.0


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

* [PATCH v4 17/21] spi: dw: Add memory operations support
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (15 preceding siblings ...)
  2020-10-07 23:55 ` [PATCH v4 16/21] spi: dw: Add generic DW SSI status-check method Serge Semin
@ 2020-10-07 23:55 ` Serge Semin
  2020-10-07 23:55 ` [PATCH v4 18/21] spi: dw: Introduce max mem-ops SPI bus frequency setting Serge Semin
                   ` (4 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:55 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

Aside from the synchronous Tx-Rx mode, which has been utilized to create
the normal SPI transfers in the framework of the DW SSI driver, DW SPI
controller supports Tx-only and EEPROM-read modes. The former one just
enables the controller to transmit all the data from the Tx FIFO ignoring
anything retrieved from the MISO lane. The later mode is so called
write-then-read operation: DW SPI controller first pushes out all the data
from the Tx FIFO, after that it'll automatically receive as much data as
has been specified by means of the CTRLR1 register. Both of those modes
can be used to implement the memory operations supported by the SPI-memory
subsystem.

The memory operation implementation is pretty much straightforward, except
a few peculiarities we have had to take into account to make things
working. Since DW SPI controller doesn't provide a way to directly set and
clear the native CS lane level, but instead automatically de-asserts it
when a transfer going on, we have to make sure the Tx FIFO isn't empty
during entire Tx procedure. In addition we also need to read data from the
Rx FIFO as fast as possible to prevent it' overflow with automatically
fetched incoming traffic. The denoted peculiarities get to cause even more
problems if DW SSI controller is equipped with relatively small FIFO and
is connected to a relatively slow system bus (APB) (with respect to the
SPI bus speed). In order to workaround the problems for as much as it's
possible, the memory operation execution procedure collects all the Tx
data into a single buffer and disables the local IRQs to speed the
write-then-optionally-read method up.

Note the provided memory operations are utilized by default only if
a glue driver hasn't provided a custom version of ones and this is not
a DW APB SSI controller with fixed automatic CS toggle functionality.

Co-developed-by: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
Signed-off-by: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/Kconfig       |   1 +
 drivers/spi/spi-dw-core.c | 301 ++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi-dw.h      |  13 ++
 3 files changed, 315 insertions(+)

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index c6ea760ea5f0..1f70bb1e7fa9 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -235,6 +235,7 @@ config SPI_DAVINCI
 
 config SPI_DESIGNWARE
 	tristate "DesignWare SPI controller core support"
+	imply SPI_MEM
 	help
 	  general driver for SPI controller core from DesignWare
 
diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 8480da49a6a1..8eb3b31b376d 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -8,10 +8,13 @@
 #include <linux/dma-mapping.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/preempt.h>
 #include <linux/highmem.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/spi/spi.h>
+#include <linux/spi/spi-mem.h>
+#include <linux/string.h>
 #include <linux/of.h>
 
 #include "spi-dw.h"
@@ -422,6 +425,301 @@ static void dw_spi_handle_err(struct spi_controller *master,
 	spi_reset_chip(dws);
 }
 
+static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+	if (op->data.dir == SPI_MEM_DATA_IN)
+		op->data.nbytes = clamp_val(op->data.nbytes, 0, SPI_NDF_MASK + 1);
+
+	return 0;
+}
+
+static bool dw_spi_supports_mem_op(struct spi_mem *mem,
+				   const struct spi_mem_op *op)
+{
+	if (op->data.buswidth > 1 || op->addr.buswidth > 1 ||
+	    op->dummy.buswidth > 1 || op->cmd.buswidth > 1)
+		return false;
+
+	return spi_mem_default_supports_op(mem, op);
+}
+
+static int dw_spi_init_mem_buf(struct dw_spi *dws, const struct spi_mem_op *op)
+{
+	unsigned int i, j, len;
+	u8 *out;
+
+	/*
+	 * Calculate the total length of the EEPROM command transfer and
+	 * either use the pre-allocated buffer or create a temporary one.
+	 */
+	len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
+	if (op->data.dir == SPI_MEM_DATA_OUT)
+		len += op->data.nbytes;
+
+	if (len <= SPI_BUF_SIZE) {
+		out = dws->buf;
+	} else {
+		out = kzalloc(len, GFP_KERNEL);
+		if (!out)
+			return -ENOMEM;
+	}
+
+	/*
+	 * Collect the operation code, address and dummy bytes into the single
+	 * buffer. If it's a transfer with data to be sent, also copy it into the
+	 * single buffer in order to speed the data transmission up.
+	 */
+	for (i = 0; i < op->cmd.nbytes; ++i)
+		out[i] = SPI_GET_BYTE(op->cmd.opcode, op->cmd.nbytes - i - 1);
+	for (j = 0; j < op->addr.nbytes; ++i, ++j)
+		out[i] = SPI_GET_BYTE(op->addr.val, op->addr.nbytes - j - 1);
+	for (j = 0; j < op->dummy.nbytes; ++i, ++j)
+		out[i] = 0x0;
+
+	if (op->data.dir == SPI_MEM_DATA_OUT)
+		memcpy(&out[i], op->data.buf.out, op->data.nbytes);
+
+	dws->n_bytes = 1;
+	dws->tx = out;
+	dws->tx_len = len;
+	if (op->data.dir == SPI_MEM_DATA_IN) {
+		dws->rx = op->data.buf.in;
+		dws->rx_len = op->data.nbytes;
+	} else {
+		dws->rx = NULL;
+		dws->rx_len = 0;
+	}
+
+	return 0;
+}
+
+static void dw_spi_free_mem_buf(struct dw_spi *dws)
+{
+	if (dws->tx != dws->buf)
+		kfree(dws->tx);
+}
+
+static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi)
+{
+	u32 room, entries, sts;
+	unsigned int len;
+	u8 *buf;
+
+	/*
+	 * At initial stage we just pre-fill the Tx FIFO in with no rush,
+	 * since native CS hasn't been enabled yet and the automatic data
+	 * transmission won't start til we do that.
+	 */
+	len = min(dws->fifo_len, dws->tx_len);
+	buf = dws->tx;
+	while (len--)
+		dw_write_io_reg(dws, DW_SPI_DR, *buf++);
+
+	/*
+	 * After setting any bit in the SER register the transmission will
+	 * start automatically. We have to keep up with that procedure
+	 * otherwise the CS de-assertion will happen whereupon the memory
+	 * operation will be pre-terminated.
+	 */
+	len = dws->tx_len - ((void *)buf - dws->tx);
+	dw_spi_set_cs(spi, false);
+	while (len) {
+		entries = readl_relaxed(dws->regs + DW_SPI_TXFLR);
+		if (!entries) {
+			dev_err(&dws->master->dev, "CS de-assertion on Tx\n");
+			return -EIO;
+		}
+		room = min(dws->fifo_len - entries, len);
+		for (; room; --room, --len)
+			dw_write_io_reg(dws, DW_SPI_DR, *buf++);
+	}
+
+	/*
+	 * Data fetching will start automatically if the EEPROM-read mode is
+	 * activated. We have to keep up with the incoming data pace to
+	 * prevent the Rx FIFO overflow causing the inbound data loss.
+	 */
+	len = dws->rx_len;
+	buf = dws->rx;
+	while (len) {
+		entries = readl_relaxed(dws->regs + DW_SPI_RXFLR);
+		if (!entries) {
+			sts = readl_relaxed(dws->regs + DW_SPI_RISR);
+			if (sts & SPI_INT_RXOI) {
+				dev_err(&dws->master->dev, "FIFO overflow on Rx\n");
+				return -EIO;
+			}
+			continue;
+		}
+		entries = min(entries, len);
+		for (; entries; --entries, --len)
+			*buf++ = dw_read_io_reg(dws, DW_SPI_DR);
+	}
+
+	return 0;
+}
+
+static inline bool dw_spi_ctlr_busy(struct dw_spi *dws)
+{
+	return dw_readl(dws, DW_SPI_SR) & SR_BUSY;
+}
+
+static int dw_spi_wait_mem_op_done(struct dw_spi *dws)
+{
+	int retry = SPI_WAIT_RETRIES;
+	struct spi_delay delay;
+	unsigned long ns, us;
+	u32 nents;
+
+	nents = dw_readl(dws, DW_SPI_TXFLR);
+	ns = NSEC_PER_SEC / dws->current_freq * nents;
+	ns *= dws->n_bytes * BITS_PER_BYTE;
+	if (ns <= NSEC_PER_USEC) {
+		delay.unit = SPI_DELAY_UNIT_NSECS;
+		delay.value = ns;
+	} else {
+		us = DIV_ROUND_UP(ns, NSEC_PER_USEC);
+		delay.unit = SPI_DELAY_UNIT_USECS;
+		delay.value = clamp_val(us, 0, USHRT_MAX);
+	}
+
+	while (dw_spi_ctlr_busy(dws) && retry--)
+		spi_delay_exec(&delay, NULL);
+
+	if (retry < 0) {
+		dev_err(&dws->master->dev, "Mem op hanged up\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void dw_spi_stop_mem_op(struct dw_spi *dws, struct spi_device *spi)
+{
+	spi_enable_chip(dws, 0);
+	dw_spi_set_cs(spi, true);
+	spi_enable_chip(dws, 1);
+}
+
+/*
+ * The SPI memory operation implementation below is the best choice for the
+ * devices, which are selected by the native chip-select lane. It's
+ * specifically developed to workaround the problem with automatic chip-select
+ * lane toggle when there is no data in the Tx FIFO buffer. Luckily the current
+ * SPI-mem core calls exec_op() callback only if the GPIO-based CS is
+ * unavailable.
+ */
+static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+	struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller);
+	struct dw_spi_cfg cfg;
+	unsigned long flags;
+	int ret;
+
+	/*
+	 * Collect the outbound data into a single buffer to speed the
+	 * transmission up at least on the initial stage.
+	 */
+	ret = dw_spi_init_mem_buf(dws, op);
+	if (ret)
+		return ret;
+
+	/*
+	 * DW SPI EEPROM-read mode is required only for the SPI memory Data-IN
+	 * operation. Transmit-only mode is suitable for the rest of them.
+	 */
+	cfg.dfs = 8;
+	cfg.freq = mem->spi->max_speed_hz;
+	if (op->data.dir == SPI_MEM_DATA_IN) {
+		cfg.tmode = SPI_TMOD_EPROMREAD;
+		cfg.ndf = op->data.nbytes;
+	} else {
+		cfg.tmode = SPI_TMOD_TO;
+	}
+
+	spi_enable_chip(dws, 0);
+
+	dw_spi_update_config(dws, mem->spi, &cfg);
+
+	spi_mask_intr(dws, 0xff);
+
+	spi_enable_chip(dws, 1);
+
+	/*
+	 * DW APB SSI controller has very nasty peculiarities. First originally
+	 * (without any vendor-specific modifications) it doesn't provide a
+	 * direct way to set and clear the native chip-select signal. Instead
+	 * the controller asserts the CS lane if Tx FIFO isn't empty and a
+	 * transmission is going on, and automatically de-asserts it back to
+	 * the high level if the Tx FIFO doesn't have anything to be pushed
+	 * out. Due to that a multi-tasking or heavy IRQs activity might be
+	 * fatal, since the transfer procedure preemption may cause the Tx FIFO
+	 * getting empty and sudden CS de-assertion, which in the middle of the
+	 * transfer will most likely cause the data loss. Secondly the
+	 * EEPROM-read or Read-only DW SPI transfer modes imply the incoming
+	 * data being automatically pulled in into the Rx FIFO. So if the
+	 * driver software is late in fetching the data from the FIFO before
+	 * it's overflown, new incoming data will be lost. In order to make
+	 * sure the executed memory operations are CS-atomic and to prevent the
+	 * Rx FIFO overflow we have to disable the local interrupts so to block
+	 * any preemption during the subsequent IO operations.
+	 *
+	 * Note. At some circumstances disabling IRQs may not help to prevent
+	 * the problems described above. The CS de-assertion and Rx FIFO
+	 * overflow may still happen due to the relatively slow system bus or
+	 * CPU not working fast enough, so the write-then-read algo implemented
+	 * here just won't keep up with the SPI bus data transfer. Such
+	 * situation is highly platform specific and is supposed to be fixed by
+	 * manually restricting the SPI bus frequency using the
+	 * dws->max_mem_freq parameter.
+	 */
+	local_irq_save(flags);
+	preempt_disable();
+
+	ret = dw_spi_write_then_read(dws, mem->spi);
+
+	local_irq_restore(flags);
+	preempt_enable();
+
+	/*
+	 * Wait for the operation being finished and check the controller
+	 * status only if there hasn't been any run-time error detected. In the
+	 * former case it's just pointless. In the later one to prevent an
+	 * additional error message printing since any hw error flag being set
+	 * would be due to an error detected on the data transfer.
+	 */
+	if (!ret) {
+		ret = dw_spi_wait_mem_op_done(dws);
+		if (!ret)
+			ret = dw_spi_check_status(dws, true);
+	}
+
+	dw_spi_stop_mem_op(dws, mem->spi);
+
+	dw_spi_free_mem_buf(dws);
+
+	return ret;
+}
+
+/*
+ * Initialize the default memory operations if a glue layer hasn't specified
+ * custom ones. Direct mapping operations will be preserved anyway since DW SPI
+ * controller doesn't have an embedded dirmap interface. Note the memory
+ * operations implemented in this driver is the best choice only for the DW APB
+ * SSI controller with standard native CS functionality. If a hardware vendor
+ * has fixed the automatic CS assertion/de-assertion peculiarity, then it will
+ * be safer to use the normal SPI-messages-based transfers implementation.
+ */
+static void dw_spi_init_mem_ops(struct dw_spi *dws)
+{
+	if (!dws->mem_ops.exec_op && !(dws->caps & DW_SPI_CAP_CS_OVERRIDE) &&
+	    !dws->set_cs) {
+		dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
+		dws->mem_ops.supports_op = dw_spi_supports_mem_op;
+		dws->mem_ops.exec_op = dw_spi_exec_mem_op;
+	}
+}
+
 /* This may be called twice for each spi dev */
 static int dw_spi_setup(struct spi_device *spi)
 {
@@ -522,6 +820,8 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
 		goto err_free_master;
 	}
 
+	dw_spi_init_mem_ops(dws);
+
 	master->use_gpio_descriptors = true;
 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
 	master->bits_per_word_mask =  SPI_BPW_RANGE_MASK(4, 16);
@@ -535,6 +835,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
 		master->set_cs = dw_spi_set_cs;
 	master->transfer_one = dw_spi_transfer_one;
 	master->handle_err = dw_spi_handle_err;
+	master->mem_ops = &dws->mem_ops;
 	master->max_speed_hz = dws->max_freq;
 	master->dev.of_node = dev->of_node;
 	master->dev.fwnode = dev->fwnode;
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 5eb98ece2f2a..4b08fe34a85d 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -8,6 +8,7 @@
 #include <linux/irqreturn.h>
 #include <linux/io.h>
 #include <linux/scatterlist.h>
+#include <linux/spi/spi-mem.h>
 
 /* Register offsets */
 #define DW_SPI_CTRLR0			0x00
@@ -78,6 +79,9 @@
  */
 #define DWC_SSI_CTRLR0_KEEMBAY_MST	BIT(31)
 
+/* Bit fields in CTRLR1 */
+#define SPI_NDF_MASK			GENMASK(15, 0)
+
 /* Bit fields in SR, 7 bits */
 #define SR_MASK				0x7f		/* cover 7 bits */
 #define SR_BUSY				(1 << 0)
@@ -101,6 +105,11 @@
 #define SPI_DMA_TDMAE			(1 << 1)
 
 #define SPI_WAIT_RETRIES		5
+#define SPI_BUF_SIZE \
+	(sizeof_field(struct spi_mem_op, cmd.opcode) + \
+	 sizeof_field(struct spi_mem_op, addr.val) + 256)
+#define SPI_GET_BYTE(_val, _idx) \
+	((_val) >> (BITS_PER_BYTE * (_idx)) & 0xff)
 
 enum dw_ssi_type {
 	SSI_MOTO_SPI = 0,
@@ -153,6 +162,7 @@ struct dw_spi {
 	unsigned int		tx_len;
 	void			*rx;
 	unsigned int		rx_len;
+	u8			buf[SPI_BUF_SIZE];
 	int			dma_mapped;
 	u8			n_bytes;	/* current is a 1/2 bytes op */
 	irqreturn_t		(*transfer_handler)(struct dw_spi *dws);
@@ -160,6 +170,9 @@ struct dw_spi {
 	u32			cur_rx_sample_dly;
 	u32			def_rx_sample_dly_ns;
 
+	/* Custom memory operations */
+	struct spi_controller_mem_ops mem_ops;
+
 	/* DMA info */
 	struct dma_chan		*txchan;
 	u32			txburst;
-- 
2.27.0


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

* [PATCH v4 18/21] spi: dw: Introduce max mem-ops SPI bus frequency setting
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (16 preceding siblings ...)
  2020-10-07 23:55 ` [PATCH v4 17/21] spi: dw: Add memory operations support Serge Semin
@ 2020-10-07 23:55 ` Serge Semin
  2020-10-07 23:55 ` [PATCH v4 19/21] spi: dw: Add poll-based SPI transfers support Serge Semin
                   ` (3 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:55 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

In some circumstances the current implementation of the SPI memory
operations may occasionally fail even though they are executed in the
atomic context. This may happen if the system bus is relatively slow in
comparison to the SPI bus frequency, or there is a concurrent access to
it, which makes the MMIO-operations occasionally stalling before
push-pulling data from the DW APB SPI FIFOs. These two problems we've
discovered on the Baikal-T1 SoC. In order to fix them we have no choice
but to set an artificial limitation on the SPI bus speed.

Note currently this limitation will be only applicable for the memory
operations, since the standard SPI core interface is implemented with an
assumption that there is no problem with the automatic CS toggling.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 4 +++-
 drivers/spi/spi-dw.h      | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 8eb3b31b376d..bcfa224e0e43 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -629,7 +629,7 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
 	 * operation. Transmit-only mode is suitable for the rest of them.
 	 */
 	cfg.dfs = 8;
-	cfg.freq = mem->spi->max_speed_hz;
+	cfg.freq = clamp(mem->spi->max_speed_hz, 0U, dws->max_mem_freq);
 	if (op->data.dir == SPI_MEM_DATA_IN) {
 		cfg.tmode = SPI_TMOD_EPROMREAD;
 		cfg.ndf = op->data.nbytes;
@@ -717,6 +717,8 @@ static void dw_spi_init_mem_ops(struct dw_spi *dws)
 		dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
 		dws->mem_ops.supports_op = dw_spi_supports_mem_op;
 		dws->mem_ops.exec_op = dw_spi_exec_mem_op;
+		if (!dws->max_mem_freq)
+			dws->max_mem_freq = dws->max_freq;
 	}
 }
 
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 4b08fe34a85d..dc5781236cc6 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -148,6 +148,7 @@ struct dw_spi {
 	unsigned long		paddr;
 	int			irq;
 	u32			fifo_len;	/* depth of the FIFO buffer */
+	u32			max_mem_freq;	/* max mem-ops bus freq */
 	u32			max_freq;	/* max bus freq supported */
 
 	u32			caps;		/* DW SPI capabilities */
-- 
2.27.0


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

* [PATCH v4 19/21] spi: dw: Add poll-based SPI transfers support
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (17 preceding siblings ...)
  2020-10-07 23:55 ` [PATCH v4 18/21] spi: dw: Introduce max mem-ops SPI bus frequency setting Serge Semin
@ 2020-10-07 23:55 ` Serge Semin
  2020-10-07 23:55 ` [PATCH v4 20/21] dt-bindings: spi: dw: Add Baikal-T1 SPI Controllers Serge Semin
                   ` (2 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:55 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

A functionality of the poll-based transfer has been removed by
commit 1ceb09717e98 ("spi: dw: remove cs_control and poll_mode members
from chip_data") with a justification that "there is no user of one
anymore". It turns out one of our DW APB SSI core is synthesized with no
IRQ line attached and the only possible way of using it is to implement a
poll-based SPI transfer procedure. So we have to get the removed
functionality back, but with some alterations described below.

First of all the poll-based transfer is activated only if the DW SPI
controller doesn't have an IRQ line attached and the Linux IRQ number is
initialized with the IRQ_NOTCONNECTED value. Secondly the transfer
procedure is now executed with a delay performed between writer and reader
methods. The delay value is calculated based on the number of data words
expected to be received on the current iteration. Finally the errors
status is checked on each iteration.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/spi-dw-core.c | 40 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index bcfa224e0e43..2e50cc0a9291 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -364,6 +364,42 @@ static void dw_spi_irq_setup(struct dw_spi *dws)
 	dws->transfer_handler = dw_spi_transfer_handler;
 }
 
+/*
+ * The iterative procedure of the poll-based transfer is simple: write as much
+ * as possible to the Tx FIFO, wait until the pending to receive data is ready
+ * to be read, read it from the Rx FIFO and check whether the performed
+ * procedure has been successful.
+ *
+ * Note this method the same way as the IRQ-based transfer won't work well for
+ * the SPI devices connected to the controller with native CS due to the
+ * automatic CS assertion/de-assertion.
+ */
+static int dw_spi_poll_transfer(struct dw_spi *dws,
+				struct spi_transfer *transfer)
+{
+	struct spi_delay delay;
+	u16 nbits;
+	int ret;
+
+	delay.unit = SPI_DELAY_UNIT_SCK;
+	nbits = dws->n_bytes * BITS_PER_BYTE;
+
+	do {
+		dw_writer(dws);
+
+		delay.value = nbits * (dws->rx_len - dws->tx_len);
+		spi_delay_exec(&delay, transfer);
+
+		dw_reader(dws);
+
+		ret = dw_spi_check_status(dws, true);
+		if (ret)
+			return ret;
+	} while (dws->rx_len);
+
+	return 0;
+}
+
 static int dw_spi_transfer_one(struct spi_controller *master,
 		struct spi_device *spi, struct spi_transfer *transfer)
 {
@@ -408,6 +444,8 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 
 	if (dws->dma_mapped)
 		return dws->dma_ops->dma_transfer(dws, transfer);
+	else if (dws->irq == IRQ_NOTCONNECTED)
+		return dw_spi_poll_transfer(dws, transfer);
 
 	dw_spi_irq_setup(dws);
 
@@ -817,7 +855,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
 
 	ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev),
 			  master);
-	if (ret < 0) {
+	if (ret < 0 && ret != -ENOTCONN) {
 		dev_err(dev, "can not get IRQ\n");
 		goto err_free_master;
 	}
-- 
2.27.0


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

* [PATCH v4 20/21] dt-bindings: spi: dw: Add Baikal-T1 SPI Controllers
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (18 preceding siblings ...)
  2020-10-07 23:55 ` [PATCH v4 19/21] spi: dw: Add poll-based SPI transfers support Serge Semin
@ 2020-10-07 23:55 ` Serge Semin
  2020-10-07 23:55 ` [PATCH v4 21/21] spi: dw: Add Baikal-T1 SPI Controller glue driver Serge Semin
  2020-10-08 22:02 ` [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Mark Brown
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:55 UTC (permalink / raw)
  To: Mark Brown, Serge Semin, Rob Herring
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, linux-spi, devicetree, linux-kernel, Rob Herring

These controllers are based on the DW APB SSI IP-core and embedded into
the SoC, so two of them are equipped with IRQ, DMA, 64 words FIFOs and 4
native CS, while another one as being utilized by the Baikal-T1 System
Boot Controller has got a very limited resources: no IRQ, no DMA, only a
single native chip-select and just 8 bytes Tx/Rx FIFOs available. That's
why we have to mark the IRQ to be optional for the later interface.

The SPI controller embedded into the Baikal-T1 System Boot Controller can
be also used to directly access an external SPI flash by means of a
dedicated FSM. The corresponding MMIO region availability is switchable by
the embedded multiplexor, which phandle can be specified in the dts node.

* We added a new example to test out the non-standard Baikal-T1 System
Boot SPI Controller DT binding.

Co-developed-by: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
Signed-off-by: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/spi/snps,dw-apb-ssi.yaml         | 33 +++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
index c62cbe79f00d..d6ae35777dac 100644
--- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
+++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
@@ -22,6 +22,21 @@ allOf:
       properties:
         reg:
           minItems: 2
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - baikal,bt1-sys-ssi
+    then:
+      properties:
+        mux-controls:
+          maxItems: 1
+      required:
+        - mux-controls
+    else:
+      required:
+        - interrupts
 
 properties:
   compatible:
@@ -44,12 +59,16 @@ properties:
           - const: snps,dw-apb-ssi
       - description: Intel Keem Bay SPI Controller
         const: intel,keembay-ssi
+      - description: Baikal-T1 SPI Controller
+        const: baikal,bt1-ssi
+      - description: Baikal-T1 System Boot SPI Controller
+        const: baikal,bt1-sys-ssi
 
   reg:
     minItems: 1
     items:
       - description: DW APB SSI controller memory mapped registers
-      - description: SPI MST region map
+      - description: SPI MST region map or directly mapped SPI ROM
 
   interrupts:
     maxItems: 1
@@ -114,7 +133,6 @@ required:
   - reg
   - "#address-cells"
   - "#size-cells"
-  - interrupts
   - clocks
 
 examples:
@@ -130,4 +148,15 @@ examples:
       cs-gpios = <&gpio0 13 0>,
                  <&gpio0 14 0>;
     };
+  - |
+    spi@1f040100 {
+      compatible = "baikal,bt1-sys-ssi";
+      reg = <0x1f040100 0x900>,
+            <0x1c000000 0x1000000>;
+      #address-cells = <1>;
+      #size-cells = <0>;
+      mux-controls = <&boot_mux>;
+      clocks = <&ccu_sys>;
+      clock-names = "ssi_clk";
+    };
 ...
-- 
2.27.0


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

* [PATCH v4 21/21] spi: dw: Add Baikal-T1 SPI Controller glue driver
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (19 preceding siblings ...)
  2020-10-07 23:55 ` [PATCH v4 20/21] dt-bindings: spi: dw: Add Baikal-T1 SPI Controllers Serge Semin
@ 2020-10-07 23:55 ` Serge Semin
  2020-10-08 22:02 ` [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Mark Brown
  21 siblings, 0 replies; 23+ messages in thread
From: Serge Semin @ 2020-10-07 23:55 UTC (permalink / raw)
  To: Mark Brown, Serge Semin
  Cc: Serge Semin, Alexey Malahov, Ramil Zaripov, Pavel Parkhomenko,
	Andy Shevchenko, Andy Shevchenko, Lars Povlsen, wuxu . wu,
	Feng Tang, Rob Herring, linux-spi, devicetree, linux-kernel

Baikal-T1 is equipped with three DW APB SSI-based MMIO SPI controllers.
Two of them are pretty much normal: with IRQ, DMA, FIFOs of 64 words
depth, 4x CSs, but the third one as being a part of the Baikal-T1 System
Boot Controller has got a very limited resources: no IRQ, no DMA, only a
single native chip-select and Tx/Rx FIFO with just 8 words depth
available. In order to provide a transparent initial boot code execution
the Boot SPI controller is also utilized by an vendor-specific IP-block,
which exposes an SPI flash direct mapping interface. Since both direct
mapping and SPI controller normal utilization are mutual exclusive only
one of these interfaces can be used to access an external SPI slave
device. That's why a dedicated mux is embedded into the System Boot
Controller. All of that is taken into account in the Baikal-T1-specific DW
APB SSI glue driver implemented by means of the DW SPI core module.

Co-developed-by: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
Signed-off-by: Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
 drivers/spi/Kconfig      |  28 ++++
 drivers/spi/Makefile     |   1 +
 drivers/spi/spi-dw-bt1.c | 339 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 368 insertions(+)
 create mode 100644 drivers/spi/spi-dw-bt1.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 1f70bb1e7fa9..415d57b2057f 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -252,6 +252,34 @@ config SPI_DW_MMIO
 	tristate "Memory-mapped io interface driver for DW SPI core"
 	depends on HAS_IOMEM
 
+config SPI_DW_BT1
+	tristate "Baikal-T1 SPI driver for DW SPI core"
+	depends on MIPS_BAIKAL_T1 || COMPILE_TEST
+	help
+	  Baikal-T1 SoC is equipped with three DW APB SSI-based MMIO SPI
+	  controllers. Two of them are pretty much normal: with IRQ, DMA,
+	  FIFOs of 64 words depth, 4x CSs, but the third one as being a
+	  part of the Baikal-T1 System Boot Controller has got a very
+	  limited resources: no IRQ, no DMA, only a single native
+	  chip-select and Tx/Rx FIFO with just 8 words depth available.
+	  The later one is normally connected to an external SPI-nor flash
+	  of 128Mb (in general can be of bigger size).
+
+config SPI_DW_BT1_DIRMAP
+	bool "Directly mapped Baikal-T1 Boot SPI flash support"
+	depends on SPI_DW_BT1
+	select MULTIPLEXER
+	select MUX_MMIO
+	help
+	  Directly mapped SPI flash memory is an interface specific to the
+	  Baikal-T1 System Boot Controller. It is a 16MB MMIO region, which
+	  can be used to access a peripheral memory device just by
+	  reading/writing data from/to it. Note that the system APB bus
+	  will stall during each IO from/to the dirmap region until the
+	  operation is finished. So try not to use it concurrently with
+	  time-critical tasks (like the SPI memory operations implemented
+	  in this driver).
+
 endif
 
 config SPI_DLN2
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index cf955ea803cd..21dc75842aca 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_SPI_DLN2)			+= spi-dln2.o
 obj-$(CONFIG_SPI_DESIGNWARE)		+= spi-dw.o
 spi-dw-y				:= spi-dw-core.o
 spi-dw-$(CONFIG_SPI_DW_DMA)		+= spi-dw-dma.o
+obj-$(CONFIG_SPI_DW_BT1)		+= spi-dw-bt1.o
 obj-$(CONFIG_SPI_DW_MMIO)		+= spi-dw-mmio.o
 obj-$(CONFIG_SPI_DW_PCI)		+= spi-dw-pci.o
 obj-$(CONFIG_SPI_EFM32)			+= spi-efm32.o
diff --git a/drivers/spi/spi-dw-bt1.c b/drivers/spi/spi-dw-bt1.c
new file mode 100644
index 000000000000..f382dfad7842
--- /dev/null
+++ b/drivers/spi/spi-dw-bt1.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+//
+// Authors:
+//   Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru>
+//   Serge Semin <Sergey.Semin@baikalelectronics.ru>
+//
+// Baikal-T1 DW APB SPI and System Boot SPI driver
+//
+
+#include <linux/clk.h>
+#include <linux/cpumask.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mux/consumer.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/spi/spi-mem.h>
+#include <linux/spi/spi.h>
+
+#include "spi-dw.h"
+
+#define BT1_BOOT_DIRMAP		0
+#define BT1_BOOT_REGS		1
+
+struct dw_spi_bt1 {
+	struct dw_spi		dws;
+	struct clk		*clk;
+	struct mux_control	*mux;
+
+#ifdef CONFIG_SPI_DW_BT1_DIRMAP
+	void __iomem		*map;
+	resource_size_t		map_len;
+#endif
+};
+#define to_dw_spi_bt1(_ctlr) \
+	container_of(spi_controller_get_devdata(_ctlr), struct dw_spi_bt1, dws)
+
+typedef int (*dw_spi_bt1_init_cb)(struct platform_device *pdev,
+				    struct dw_spi_bt1 *dwsbt1);
+
+#ifdef CONFIG_SPI_DW_BT1_DIRMAP
+
+static int dw_spi_bt1_dirmap_create(struct spi_mem_dirmap_desc *desc)
+{
+	struct dw_spi_bt1 *dwsbt1 = to_dw_spi_bt1(desc->mem->spi->controller);
+
+	if (!dwsbt1->map ||
+	    !dwsbt1->dws.mem_ops.supports_op(desc->mem, &desc->info.op_tmpl))
+		return -EOPNOTSUPP;
+
+	/*
+	 * Make sure the requested region doesn't go out of the physically
+	 * mapped flash memory bounds and the operation is read-only.
+	 */
+	if (desc->info.offset + desc->info.length > dwsbt1->map_len ||
+	    desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+/*
+ * Directly mapped SPI memory region is only accessible in the dword chunks.
+ * That's why we have to create a dedicated read-method to copy data from there
+ * to the passed buffer.
+ */
+static void dw_spi_bt1_dirmap_copy_from_map(void *to, void __iomem *from, size_t len)
+{
+	size_t shift, chunk;
+	u32 data;
+
+	/*
+	 * We split the copying up into the next three stages: unaligned head,
+	 * aligned body, unaligned tail.
+	 */
+	shift = (size_t)from & 0x3;
+	if (shift) {
+		chunk = min_t(size_t, 4 - shift, len);
+		data = readl_relaxed(from - shift);
+		memcpy(to, &data + shift, chunk);
+		from += chunk;
+		to += chunk;
+		len -= chunk;
+	}
+
+	while (len >= 4) {
+		data = readl_relaxed(from);
+		memcpy(to, &data, 4);
+		from += 4;
+		to += 4;
+		len -= 4;
+	}
+
+	if (len) {
+		data = readl_relaxed(from);
+		memcpy(to, &data, len);
+	}
+}
+
+static ssize_t dw_spi_bt1_dirmap_read(struct spi_mem_dirmap_desc *desc,
+				      u64 offs, size_t len, void *buf)
+{
+	struct dw_spi_bt1 *dwsbt1 = to_dw_spi_bt1(desc->mem->spi->controller);
+	struct dw_spi *dws = &dwsbt1->dws;
+	struct spi_mem *mem = desc->mem;
+	struct dw_spi_cfg cfg;
+	int ret;
+
+	/*
+	 * Make sure the requested operation length is valid. Truncate the
+	 * length if it's greater than the length of the MMIO region.
+	 */
+	if (offs >= dwsbt1->map_len || !len)
+		return 0;
+
+	len = min_t(size_t, len, dwsbt1->map_len - offs);
+
+	/* Collect the controller configuration required by the operation */
+	cfg.tmode = SPI_TMOD_EPROMREAD;
+	cfg.dfs = 8;
+	cfg.ndf = 4;
+	cfg.freq = mem->spi->max_speed_hz;
+
+	/* Make sure the corresponding CS is de-asserted on transmission */
+	dw_spi_set_cs(mem->spi, false);
+
+	spi_enable_chip(dws, 0);
+
+	dw_spi_update_config(dws, mem->spi, &cfg);
+
+	spi_umask_intr(dws, SPI_INT_RXFI);
+
+	spi_enable_chip(dws, 1);
+
+	/*
+	 * Enable the transparent mode of the System Boot Controller.
+	 * The SPI core IO should have been locked before calling this method
+	 * so noone would be touching the controller' registers during the
+	 * dirmap operation.
+	 */
+	ret = mux_control_select(dwsbt1->mux, BT1_BOOT_DIRMAP);
+	if (ret)
+		return ret;
+
+	dw_spi_bt1_dirmap_copy_from_map(buf, dwsbt1->map + offs, len);
+
+	mux_control_deselect(dwsbt1->mux);
+
+	dw_spi_set_cs(mem->spi, true);
+
+	ret = dw_spi_check_status(dws, true);
+
+	return ret ?: len;
+}
+
+#endif /* CONFIG_SPI_DW_BT1_DIRMAP */
+
+static int dw_spi_bt1_std_init(struct platform_device *pdev,
+			       struct dw_spi_bt1 *dwsbt1)
+{
+	struct dw_spi *dws = &dwsbt1->dws;
+
+	dws->irq = platform_get_irq(pdev, 0);
+	if (dws->irq < 0)
+		return dws->irq;
+
+	dws->num_cs = 4;
+
+	/*
+	 * Baikal-T1 Normal SPI Controllers don't always keep up with full SPI
+	 * bus speed especially when it comes to the concurrent access to the
+	 * APB bus resources. Thus we have no choice but to set a constraint on
+	 * the SPI bus frequency for the memory operations which require to
+	 * read/write data as fast as possible.
+	 */
+	dws->max_mem_freq = 20000000U;
+
+	dw_spi_dma_setup_generic(dws);
+
+	return 0;
+}
+
+static int dw_spi_bt1_sys_init(struct platform_device *pdev,
+			       struct dw_spi_bt1 *dwsbt1)
+{
+	struct resource *mem __maybe_unused;
+	struct dw_spi *dws = &dwsbt1->dws;
+
+	/*
+	 * Baikal-T1 System Boot Controller is equipped with a mux, which
+	 * switches between the directly mapped SPI flash access mode and
+	 * IO access to the DW APB SSI registers. Note the mux controller
+	 * must be setup to preserve the registers being accessible by default
+	 * (on idle-state).
+	 */
+	dwsbt1->mux = devm_mux_control_get(&pdev->dev, NULL);
+	if (IS_ERR(dwsbt1->mux))
+		return PTR_ERR(dwsbt1->mux);
+
+	/*
+	 * Directly mapped SPI flash memory is a 16MB MMIO region, which can be
+	 * used to access a peripheral memory device just by reading/writing
+	 * data from/to it. Note the system APB bus will stall during each IO
+	 * from/to the dirmap region until the operation is finished. So don't
+	 * use it concurrently with time-critical tasks (like the SPI memory
+	 * operations implemented in the DW APB SSI driver).
+	 */
+#ifdef CONFIG_SPI_DW_BT1_DIRMAP
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (mem) {
+		dwsbt1->map = devm_ioremap_resource(&pdev->dev, mem);
+		if (!IS_ERR(dwsbt1->map)) {
+			dwsbt1->map_len = (mem->end - mem->start + 1);
+			dws->mem_ops.dirmap_create = dw_spi_bt1_dirmap_create;
+			dws->mem_ops.dirmap_read = dw_spi_bt1_dirmap_read;
+		} else {
+			dwsbt1->map = NULL;
+		}
+	}
+#endif /* CONFIG_SPI_DW_BT1_DIRMAP */
+
+	/*
+	 * There is no IRQ, no DMA and just one CS available on the System Boot
+	 * SPI controller.
+	 */
+	dws->irq = IRQ_NOTCONNECTED;
+	dws->num_cs = 1;
+
+	/*
+	 * Baikal-T1 System Boot SPI Controller doesn't keep up with the full
+	 * SPI bus speed due to relatively slow APB bus and races for it'
+	 * resources from different CPUs. The situation is worsen by a small
+	 * FIFOs depth (just 8 words). It works better in a single CPU mode
+	 * though, but still tends to be not fast enough at low CPU
+	 * frequencies.
+	 */
+	if (num_possible_cpus() > 1)
+		dws->max_mem_freq = 10000000U;
+	else
+		dws->max_mem_freq = 20000000U;
+
+	return 0;
+}
+
+static int dw_spi_bt1_probe(struct platform_device *pdev)
+{
+	dw_spi_bt1_init_cb init_func;
+	struct dw_spi_bt1 *dwsbt1;
+	struct resource *mem;
+	struct dw_spi *dws;
+	int ret;
+
+	dwsbt1 = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_bt1), GFP_KERNEL);
+	if (!dwsbt1)
+		return -ENOMEM;
+
+	dws = &dwsbt1->dws;
+
+	dws->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+	if (IS_ERR(dws->regs))
+		return PTR_ERR(dws->regs);
+
+	dws->paddr = mem->start;
+
+	dwsbt1->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(dwsbt1->clk))
+		return PTR_ERR(dwsbt1->clk);
+
+	ret = clk_prepare_enable(dwsbt1->clk);
+	if (ret)
+		return ret;
+
+	dws->bus_num = pdev->id;
+	dws->reg_io_width = 4;
+	dws->max_freq = clk_get_rate(dwsbt1->clk);
+	if (!dws->max_freq)
+		goto err_disable_clk;
+
+	init_func = device_get_match_data(&pdev->dev);
+	ret = init_func(pdev, dwsbt1);
+	if (ret)
+		goto err_disable_clk;
+
+	pm_runtime_enable(&pdev->dev);
+
+	ret = dw_spi_add_host(&pdev->dev, dws);
+	if (ret)
+		goto err_disable_clk;
+
+	platform_set_drvdata(pdev, dwsbt1);
+
+	return 0;
+
+err_disable_clk:
+	clk_disable_unprepare(dwsbt1->clk);
+
+	return ret;
+}
+
+static int dw_spi_bt1_remove(struct platform_device *pdev)
+{
+	struct dw_spi_bt1 *dwsbt1 = platform_get_drvdata(pdev);
+
+	dw_spi_remove_host(&dwsbt1->dws);
+
+	pm_runtime_disable(&pdev->dev);
+
+	clk_disable_unprepare(dwsbt1->clk);
+
+	return 0;
+}
+
+static const struct of_device_id dw_spi_bt1_of_match[] = {
+	{ .compatible = "baikal,bt1-ssi", .data = dw_spi_bt1_std_init},
+	{ .compatible = "baikal,bt1-sys-ssi", .data = dw_spi_bt1_sys_init},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, dw_spi_bt1_of_match);
+
+static struct platform_driver dw_spi_bt1_driver = {
+	.probe	= dw_spi_bt1_probe,
+	.remove	= dw_spi_bt1_remove,
+	.driver	= {
+		.name		= "bt1-sys-ssi",
+		.of_match_table	= dw_spi_bt1_of_match,
+	},
+};
+module_platform_driver(dw_spi_bt1_driver);
+
+MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 System Boot SPI Controller driver");
+MODULE_LICENSE("GPL v2");
-- 
2.27.0


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

* Re: [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support
  2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
                   ` (20 preceding siblings ...)
  2020-10-07 23:55 ` [PATCH v4 21/21] spi: dw: Add Baikal-T1 SPI Controller glue driver Serge Semin
@ 2020-10-08 22:02 ` Mark Brown
  21 siblings, 0 replies; 23+ messages in thread
From: Mark Brown @ 2020-10-08 22:02 UTC (permalink / raw)
  To: Serge Semin
  Cc: wuxu . wu, devicetree, Alexey Malahov, Lars Povlsen,
	Andy Shevchenko, Serge Semin, Ramil Zaripov, Pavel Parkhomenko,
	Feng Tang, linux-spi, Rob Herring, Andy Shevchenko, linux-kernel

On Thu, 8 Oct 2020 02:54:49 +0300, Serge Semin wrote:
> Originally I intended to merge a dedicated Baikal-T1 System Boot SPI
> Controller driver into the kernel and leave the DW APB SSI driver
> untouched. But after a long discussion (see the link at the bottom of the
> letter) Mark and Andy persuaded me to integrate what we developed there
> into the DW APB SSI core driver to be useful for another controllers,
> which may have got the same peculiarities/problems as ours:
> - No IRQ.
> - No DMA.
> - No GPIO CS, so a native CS is utilized.
> - small Tx/Rx FIFO depth.
> - Automatic CS assertion/de-assertion.
> - Slow system bus.
> All of them have been fixed in the framework of this patchset in some
> extent at least for the SPI memory operations. As I expected it wasn't
> that easy and the integration took that many patches as you can see from
> the subject. Though some of them are mere cleanups or weakly related with
> the subject fixes, but we just couldn't leave the code as is at some
> places since we were working with the DW APB SSI driver anyway. Here is
> what we did to fix the original DW APB SSI driver, to make it less messy.
> 
> [...]

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next

Thanks!

[01/21] spi: dw: Use an explicit set_cs assignment
        commit: f68fe8de360b9c441caf22f35557b4c9fd97dd84
[02/21] spi: dw: Add DWC SSI capability
        commit: d6bbd1193fe93b7ee037724553b3574dcb48e6da
[03/21] spi: dw: Detach SPI device specific CR0 config method
        commit: a3577bd8cba554f962b6af082eb43dde7fe7cd09
[04/21] spi: dw: Update SPI bus speed in a config function
        commit: f76f3142c5fc90f67794f6649cecec86a6eb87b0
[05/21] spi: dw: Simplify the SPI bus speed config procedure
        commit: c449ad7425aa2eb58f275ce977130918827b0d20
[06/21] spi: dw: Update Rx sample delay in the config function
        commit: 2613d2bfbeacea2bc796a54219ba05385ae7436a
[07/21] spi: dw: Add DW SPI controller config structure
        commit: 3ff60c6b644e2002e062ed97825ead19e31c2769
[08/21] spi: dw: Refactor data IO procedure
        commit: 8dedbeac8ab24d2da9271df2c8291971169846f2
[09/21] spi: dw: Refactor IRQ-based SPI transfer procedure
        commit: ddcc2733c1591c137f7ce60f24ba5401c295427f
[10/21] spi: dw: Perform IRQ setup in a dedicated function
        commit: 82d02944d238a6fdac729d135623b1d88ca8cbd6
[11/21] spi: dw: Unmask IRQs after enabling the chip
        commit: da8f58909e7e047a01e4577807e648482672eddd
[12/21] spi: dw: Discard chip enabling on DMA setup error
        commit: c6cb3815f70d39e377bec6b44d25f2d8b68b324e
[13/21] spi: dw: De-assert chip-select on reset
        commit: fbddc989a5c441099978aad320ada0d5327309f4
[14/21] spi: dw: Explicitly de-assert CS on SPI transfer completion
        commit: 49d7d695ca4bb2f62290c7039c4165556f0ca1e4
[15/21] spi: dw: Move num-of retries parameter to the header file
        commit: cf75baeac72c7cb57a8cf781e90cfd8ea77f2d51
[16/21] spi: dw: Add generic DW SSI status-check method
        commit: bf64b66036eef7d5a92fb1cb7398ef67a29fc64b
[17/21] spi: dw: Add memory operations support
        commit: 6423207e57ea53826eaae1a14c14fd6d22561b06
[18/21] spi: dw: Introduce max mem-ops SPI bus frequency setting
        commit: 84ecaf4a7837e8c0957a59d77fd7e8e4926968cb
[19/21] spi: dw: Add poll-based SPI transfers support
        commit: 14345c33461bc2373bc4f75f40baf4650e95ee54
[20/21] spi: dw: Add Baikal-T1 SPI Controller bindings
        commit: ca4e2ac20f938c372b83d1cb16ec00f7c89191df
[21/21] spi: dw: Add Baikal-T1 SPI Controller glue driver
        commit: abf00907538e21c469a10809dc2991982673fcbf

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

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

end of thread, back to index

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-07 23:54 [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Serge Semin
2020-10-07 23:54 ` [PATCH v4 01/21] spi: dw: Use an explicit set_cs assignment Serge Semin
2020-10-07 23:54 ` [PATCH v4 02/21] spi: dw: Add DWC SSI capability Serge Semin
2020-10-07 23:54 ` [PATCH v4 03/21] spi: dw: Detach SPI device specific CR0 config method Serge Semin
2020-10-07 23:54 ` [PATCH v4 04/21] spi: dw: Update SPI bus speed in a config function Serge Semin
2020-10-07 23:54 ` [PATCH v4 05/21] spi: dw: Simplify the SPI bus speed config procedure Serge Semin
2020-10-07 23:54 ` [PATCH v4 06/21] spi: dw: Update Rx sample delay in the config function Serge Semin
2020-10-07 23:54 ` [PATCH v4 07/21] spi: dw: Add DW SPI controller config structure Serge Semin
2020-10-07 23:54 ` [PATCH v4 08/21] spi: dw: Refactor data IO procedure Serge Semin
2020-10-07 23:54 ` [PATCH v4 09/21] spi: dw: Refactor IRQ-based SPI transfer procedure Serge Semin
2020-10-07 23:54 ` [PATCH v4 10/21] spi: dw: Perform IRQ setup in a dedicated function Serge Semin
2020-10-07 23:55 ` [PATCH v4 11/21] spi: dw: Unmask IRQs after enabling the chip Serge Semin
2020-10-07 23:55 ` [PATCH v4 12/21] spi: dw: Discard chip enabling on DMA setup error Serge Semin
2020-10-07 23:55 ` [PATCH v4 13/21] spi: dw: De-assert chip-select on reset Serge Semin
2020-10-07 23:55 ` [PATCH v4 14/21] spi: dw: Explicitly de-assert CS on SPI transfer completion Serge Semin
2020-10-07 23:55 ` [PATCH v4 15/21] spi: dw: Move num-of retries parameter to the header file Serge Semin
2020-10-07 23:55 ` [PATCH v4 16/21] spi: dw: Add generic DW SSI status-check method Serge Semin
2020-10-07 23:55 ` [PATCH v4 17/21] spi: dw: Add memory operations support Serge Semin
2020-10-07 23:55 ` [PATCH v4 18/21] spi: dw: Introduce max mem-ops SPI bus frequency setting Serge Semin
2020-10-07 23:55 ` [PATCH v4 19/21] spi: dw: Add poll-based SPI transfers support Serge Semin
2020-10-07 23:55 ` [PATCH v4 20/21] dt-bindings: spi: dw: Add Baikal-T1 SPI Controllers Serge Semin
2020-10-07 23:55 ` [PATCH v4 21/21] spi: dw: Add Baikal-T1 SPI Controller glue driver Serge Semin
2020-10-08 22:02 ` [PATCH v4 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support Mark Brown

Linux-SPI Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-spi/0 linux-spi/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-spi linux-spi/ https://lore.kernel.org/linux-spi \
		linux-spi@vger.kernel.org
	public-inbox-index linux-spi

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-spi


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git