[PATCH v4 11/21] spi: dw: Unmask IRQs after enabling the chip

2020-10-07 Thread Serge Semin
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 
---
 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



[PATCH v4 01/21] spi: dw: Use an explicit set_cs assignment

2020-10-07 Thread Serge Semin
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 

---

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",
 >def_rx_sample_dly_ns);
-- 
2.27.0



[PATCH v4 03/21] spi: dw: Detach SPI device specific CR0 config method

2020-10-07 Thread Serge Semin
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 

---

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



[PATCH v4 13/21] spi: dw: De-assert chip-select on reset

2020-10-07 Thread Serge Semin
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 
---
 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



[PATCH v4 18/21] spi: dw: Introduce max mem-ops SPI bus frequency setting

2020-10-07 Thread Serge Semin
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 
---
 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



[PATCH v4 16/21] spi: dw: Add generic DW SSI status-check method

2020-10-07 Thread Serge Semin
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 
---
 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(>master->dev, "RX FIFO overflow detected\n");
+   ret = -EIO;
+   }
+
+   if (irq_status & SPI_INT_RXUI) {
+   dev_err(>master->dev, "RX FIFO underflow detected\n");
+   ret = -EIO;
+   }
 
-   dev_err(>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(>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(>master->dev, "%s: FIFO overrun/underrun\n", __func__);
-   dws->master->cur_msg->status = -EIO;
complete(>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



[PATCH v4 17/21] spi: dw: Add memory operations support

2020-10-07 Thread Serge Semin
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 
Signed-off-by: Ramil Zaripov 
Signed-off-by: Serge Semin 
---
 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 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 
 #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([i], op->data.buf.out, op->dat

[PATCH v4 14/21] spi: dw: Explicitly de-assert CS on SPI transfer completion

2020-10-07 Thread Serge Semin
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 
---
 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



[PATCH v4 15/21] spi: dw: Move num-of retries parameter to the header file

2020-10-07 Thread Serge Semin
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 
---
 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_BUSY0
 #define RX_BURST_LEVEL 16
 #define TX_BUSY1
@@ -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



[PATCH v4 10/21] spi: dw: Perform IRQ setup in a dedicated function

2020-10-07 Thread Serge Semin
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 
---
 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



[PATCH] MAINTAINERS: Add maintainer of DW APB SSI driver

2020-10-02 Thread Serge Semin
Add myself as a maintainer of the Synopsis DesignWare APB SSI driver.

Suggested-by: Andy Shevchenko 
Signed-off-by: Serge Semin 
Link: 
https://lore.kernel.org/linux-spi/20201001222829.15977-1-sergey.se...@baikalelectronics.ru/
---
 MAINTAINERS | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 0d0862b19ce5..308a0843ec3c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16727,6 +16727,13 @@ S: Maintained
 F: Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml
 F: drivers/gpio/gpio-dwapb.c
 
+SYNOPSYS DESIGNWARE APB SSI DRIVER
+M: Serge Semin 
+L: linux-...@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
+F: drivers/spi/spi-dw*
+
 SYNOPSYS DESIGNWARE AXI DMAC DRIVER
 M: Eugeniy Paltsev 
 S: Maintained
-- 
2.27.0



Re: [PATCH v3 02/21] spi: dw: Add DWC SSI capability

2020-10-02 Thread Serge Semin
On Fri, Oct 02, 2020 at 11:08:29PM +0300, Serge Semin wrote:
> On Fri, Oct 02, 2020 at 10:46:09PM +0300, Serge Semin wrote:
> > On Fri, Oct 02, 2020 at 09:26:07PM +0300, Andy Shevchenko wrote:
> > > On Fri, Oct 2, 2020 at 8:18 PM Serge Semin
> > >  wrote:
> > > >
> > > > On Fri, Oct 02, 2020 at 01:19:29PM +0300, Andy Shevchenko wrote:
> > > > > On Fri, Oct 02, 2020 at 01:28:10AM +0300, Serge Semin wrote:
> > > > > > 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.
> > > > >
> > > >
> > > > > What the point to increase indentation level and produce additional 
> > > > > churn?
> > > > > Can't you simply leave functions, unexport them, and call in one 
> > > > > conditional of
> > > > > whatever new function is called?
> > > >
> > > > I forgot to mention that in the commit log, there is another reason why 
> > > > it's
> > > > better to create a generic dw_spi_update_cr0() instead of doing what 
> > > > you suggest.
> > > > As it will be seen from the following up patches, the 
> > > > dw_spi_update_cr0() function
> > > > (to be more precise it's successor, but anyway) will be used from the 
> > > > SPI memory
> > > > ops implementation. So if-else-ing here and there isn't a good idea for
> > > > maintainability. For the same reason of the maintainability it's better 
> > > > to have a
> > > > generic method which reflects all the config peculiarities, so in case 
> > > > of any
> > > > changes they would be not be forgotten to be introduced for both DWC 
> > > > SSI and DW
> > > > APB SSI parts of the setup procedures. As I see it that overbeats the 
> > > > additional
> > > > indentation level drawback.
> > > 
> > 
> > > What I meant is to leave functions as is and call them under conditional
> > > 
> > > if ()
> > >  call one
> > > else
> > >  call another
> > 
> > Yeah, I understood what you meant. What you suggest would be a better 
> > solution
> > if I needed to call the dw_spi_update_cr0() method just from a single place 
> > of
> > the driver (but in that case I wouldn't need to replace the callback-based
> > approach with the Capabilities-based one at all). The thing is that the
> > dw_spi_update_cr0() will be also called from the SPI memory exec_op() 
> > callback
> > (see patch

Re: [PATCH v3 02/21] spi: dw: Add DWC SSI capability

2020-10-02 Thread Serge Semin
On Fri, Oct 02, 2020 at 10:46:09PM +0300, Serge Semin wrote:
> On Fri, Oct 02, 2020 at 09:26:07PM +0300, Andy Shevchenko wrote:
> > On Fri, Oct 2, 2020 at 8:18 PM Serge Semin
> >  wrote:
> > >
> > > On Fri, Oct 02, 2020 at 01:19:29PM +0300, Andy Shevchenko wrote:
> > > > On Fri, Oct 02, 2020 at 01:28:10AM +0300, Serge Semin wrote:
> > > > > 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.
> > > >
> > >
> > > > What the point to increase indentation level and produce additional 
> > > > churn?
> > > > Can't you simply leave functions, unexport them, and call in one 
> > > > conditional of
> > > > whatever new function is called?
> > >
> > > I forgot to mention that in the commit log, there is another reason why 
> > > it's
> > > better to create a generic dw_spi_update_cr0() instead of doing what you 
> > > suggest.
> > > As it will be seen from the following up patches, the dw_spi_update_cr0() 
> > > function
> > > (to be more precise it's successor, but anyway) will be used from the SPI 
> > > memory
> > > ops implementation. So if-else-ing here and there isn't a good idea for
> > > maintainability. For the same reason of the maintainability it's better 
> > > to have a
> > > generic method which reflects all the config peculiarities, so in case of 
> > > any
> > > changes they would be not be forgotten to be introduced for both DWC SSI 
> > > and DW
> > > APB SSI parts of the setup procedures. As I see it that overbeats the 
> > > additional
> > > indentation level drawback.
> > 
> 
> > What I meant is to leave functions as is and call them under conditional
> > 
> > if ()
> >  call one
> > else
> >  call another
> 
> Yeah, I understood what you meant. What you suggest would be a better solution
> if I needed to call the dw_spi_update_cr0() method just from a single place of
> the driver (but in that case I wouldn't need to replace the callback-based
> approach with the Capabilities-based one at all). The thing is that the
> dw_spi_update_cr0() will be also called from the SPI memory exec_op() callback
> (see patch "[PATCH v3 17/21] spi: dw: Add memory operations support" and the
> method dw_spi_update_config() invocation) in the same way as it is called from
> the SPI core transfer-one callback. Following your suggestion I would have to
> implement the same "if () call one else call another" pattern there too. 
> Copying
> it here and there would be a weak design from the maintainability point of 
> view
> and from the coding style too. Much better is to create a generic
> dw_spi_update_cr0() (later in this patchset it will be renamed to
> dw_spi_update_config()...), which would work for both DWC SSI and DW APB SSI 
> by
> embedding the "if (is_CAP) call one else call another" into the method itself 
> as
> I suggested in this patch.

Oh, and the same "if-else" pattern would need to be either left in the
dw_spi_get_cr0()/dw_spi_prepare_cr0() or added around the dw_spi_prepare_cr0()
method invocation with creating two versions of it. So no, I'd stick with the
design I suggested in this patch: just two "if-else"s and the generic versions
of the dw_spi_prepare_cr0() and dw_spi_update_cr0() functions.

-Sergey

> 
> -Sergey
> 
> > 
> > 
> > -- 
> > With Best Regards,
> > Andy Shevchenko


Re: [PATCH v3 03/21] spi: dw: Detach SPI device specific CR0 config method

2020-10-02 Thread Serge Semin
On Fri, Oct 02, 2020 at 09:24:55PM +0300, Andy Shevchenko wrote:
> On Fri, Oct 2, 2020 at 8:47 PM Serge Semin
>  wrote:
> > On Fri, Oct 02, 2020 at 01:22:46PM +0300, Andy Shevchenko wrote:
> > > On Fri, Oct 02, 2020 at 01:28:11AM +0300, Serge Semin wrote:
> 
> > > > +   /*
> > > > +* 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_get_cr0(dws, spi);
> > >
> >
> > > I would rather name it prepare or alike. 'get' assumes getting value or
> > > something like that.
> >
> > This seems reasonable. What verb do you think would be better: prepare,
> > calc, assemble, construct, make, compute, collect, compose, form, compile, 
> > etc ?
> > Personally prepare or calc or assemble are the best candidates. What do you
> > think?
> 

> prepare is good enough if you agree on it.

Ok. "prepare" it is then.

-Sergey

> 
> 
> -- 
> With Best Regards,
> Andy Shevchenko


Re: [PATCH v3 02/21] spi: dw: Add DWC SSI capability

2020-10-02 Thread Serge Semin
On Fri, Oct 02, 2020 at 09:26:07PM +0300, Andy Shevchenko wrote:
> On Fri, Oct 2, 2020 at 8:18 PM Serge Semin
>  wrote:
> >
> > On Fri, Oct 02, 2020 at 01:19:29PM +0300, Andy Shevchenko wrote:
> > > On Fri, Oct 02, 2020 at 01:28:10AM +0300, Serge Semin wrote:
> > > > 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.
> > >
> >
> > > What the point to increase indentation level and produce additional churn?
> > > Can't you simply leave functions, unexport them, and call in one 
> > > conditional of
> > > whatever new function is called?
> >
> > I forgot to mention that in the commit log, there is another reason why it's
> > better to create a generic dw_spi_update_cr0() instead of doing what you 
> > suggest.
> > As it will be seen from the following up patches, the dw_spi_update_cr0() 
> > function
> > (to be more precise it's successor, but anyway) will be used from the SPI 
> > memory
> > ops implementation. So if-else-ing here and there isn't a good idea for
> > maintainability. For the same reason of the maintainability it's better to 
> > have a
> > generic method which reflects all the config peculiarities, so in case of 
> > any
> > changes they would be not be forgotten to be introduced for both DWC SSI 
> > and DW
> > APB SSI parts of the setup procedures. As I see it that overbeats the 
> > additional
> > indentation level drawback.
> 

> What I meant is to leave functions as is and call them under conditional
> 
> if ()
>  call one
> else
>  call another

Yeah, I understood what you meant. What you suggest would be a better solution
if I needed to call the dw_spi_update_cr0() method just from a single place of
the driver (but in that case I wouldn't need to replace the callback-based
approach with the Capabilities-based one at all). The thing is that the
dw_spi_update_cr0() will be also called from the SPI memory exec_op() callback
(see patch "[PATCH v3 17/21] spi: dw: Add memory operations support" and the
method dw_spi_update_config() invocation) in the same way as it is called from
the SPI core transfer-one callback. Following your suggestion I would have to
implement the same "if () call one else call another" pattern there too. Copying
it here and there would be a weak design from the maintainability point of view
and from the coding style too. Much better is to create a generic
dw_spi_update_cr0() (later in this patchset it will be renamed to
dw_spi_update_config()...), which would work for both DWC SSI and DW APB SSI by
embedding the "if (is_CAP) call one else call another" into the method itself as
I suggested in this patch.

-Sergey

> 
> 
> -- 
> With Best Regards,
> Andy Shevchenko


Re: [PATCH v3 03/21] spi: dw: Detach SPI device specific CR0 config method

2020-10-02 Thread Serge Semin
On Fri, Oct 02, 2020 at 01:22:46PM +0300, Andy Shevchenko wrote:
> On Fri, Oct 02, 2020 at 01:28:11AM +0300, Serge Semin wrote:
> > 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.
> 
> > +static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi,
> > + struct spi_transfer *transfer)
> 

> Yep, why not to place this in previous patch exactly here?

The previous patch is about introducing the DWC SSI capability. This one is
about splitting the functionality up.

> 
> > +   /*
> > +* 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_get_cr0(dws, spi);
> 

> I would rather name it prepare or alike. 'get' assumes getting value or
> something like that.

This seems reasonable. What verb do you think would be better: prepare,
calc, assemble, construct, make, compute, collect, compose, form, compile, etc ?
Personally prepare or calc or assemble are the best candidates. What do you
think?

-Sergey

> 
> -- 
> With Best Regards,
> Andy Shevchenko
> 
> 


Re: [PATCH v3 02/21] spi: dw: Add DWC SSI capability

2020-10-02 Thread Serge Semin
On Fri, Oct 02, 2020 at 01:19:29PM +0300, Andy Shevchenko wrote:
> On Fri, Oct 02, 2020 at 01:28:10AM +0300, Serge Semin wrote:
> > 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.
> 

> What the point to increase indentation level and produce additional churn?
> Can't you simply leave functions, unexport them, and call in one conditional 
> of
> whatever new function is called?

I forgot to mention that in the commit log, there is another reason why it's
better to create a generic dw_spi_update_cr0() instead of doing what you 
suggest.
As it will be seen from the following up patches, the dw_spi_update_cr0() 
function
(to be more precise it's successor, but anyway) will be used from the SPI memory
ops implementation. So if-else-ing here and there isn't a good idea for
maintainability. For the same reason of the maintainability it's better to have 
a
generic method which reflects all the config peculiarities, so in case of any
changes they would be not be forgotten to be introduced for both DWC SSI and DW
APB SSI parts of the setup procedures. As I see it that overbeats the additional
indentation level drawback.

-Sergey

> 
> I have an impression that split of the series is done in a way that first
> patches in the series are not optimized to what is done in the last patches in
> the series.
> 
> -- 
> With Best Regards,
> Andy Shevchenko
> 
> 


[PATCH v3 14/21] spi: dw: Explicitly de-assert CS on SPI transfer completion

2020-10-01 Thread Serge Semin
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 
---
 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 fca929280aab..a6f86314567f 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



[PATCH v3 13/21] spi: dw: De-assert chip-select on reset

2020-10-01 Thread Serge Semin
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 
---
 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



[PATCH v3 07/21] spi: dw: Add DW SPI controller config structure

2020-10-01 Thread Serge Semin
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 
---
 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 87c8f0028a23..92c26b02269b 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -20,10 +20,8 @@
 #include 
 #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_get_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, );
 
transfer->effective_speed_hz = dws->current_freq;
 
@@ -409,8 +416,6 @@ static int dw_spi_setup(struct spi_device *spi)
 */
chip->cr0 = dw_spi_get_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);
 exte

[PATCH v3 05/21] spi: dw: Simplify the SPI bus speed config procedure

2020-10-01 Thread Serge Semin
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 
---
 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 77dfd6681f0c..478262fb4f8e 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



[PATCH v3 09/21] spi: dw: Refactor IRQ-based SPI transfer procedure

2020-10-01 Thread Serge Semin
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 
---
 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 4baf72b121c2..74e8f0da2883 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



[PATCH v3 11/21] spi: dw: Unmask IRQs after enabling the chip

2020-10-01 Thread Serge Semin
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 
---
 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 db3fec4195f7..58a7c7465c61 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



[PATCH v3 10/21] spi: dw: Perform IRQ setup in a dedicated function

2020-10-01 Thread Serge Semin
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 
---
 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 74e8f0da2883..db3fec4195f7 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



[PATCH v3 12/21] spi: dw: Discard chip enabling on DMA setup error

2020-10-01 Thread Serge Semin
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 
---
 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 58a7c7465c61..fca929280aab 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



[PATCH v3 15/21] spi: dw: Move num-of retries parameter to the header file

2020-10-01 Thread Serge Semin
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 
---
 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_BUSY0
 #define RX_BURST_LEVEL 16
 #define TX_BUSY1
@@ -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



[PATCH v3 06/21] spi: dw: Update Rx sample delay in the config function

2020-10-01 Thread Serge Semin
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 
---
 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 478262fb4f8e..87c8f0028a23 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



[PATCH v3 21/21] spi: dw: Add Baikal-T1 SPI Controller glue driver

2020-10-01 Thread Serge Semin
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 
Signed-off-by: Ramil Zaripov 
Signed-off-by: Serge Semin 
---
 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 ..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 
+//   Serge Semin 
+//
+// Baikal-T1 DW APB SPI and System Boot SPI driver
+//
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "spi-dw.h"
+
+#define BT1_BOOT_DIRMAP0
+#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);

[PATCH v3 16/21] spi: dw: Add generic DW SSI status-check method

2020-10-01 Thread Serge Semin
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 
---
 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 a6f86314567f..72b205dc6c81 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(>master->dev, "RX FIFO overflow detected\n");
+   ret = -EIO;
+   }
+
+   if (irq_status & SPI_INT_RXUI) {
+   dev_err(>master->dev, "RX FIFO underflow detected\n");
+   ret = -EIO;
+   }
 
-   dev_err(>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(>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(>master->dev, "%s: FIFO overrun/underrun\n", __func__);
-   dws->master->cur_msg->status = -EIO;
complete(>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



[PATCH v3 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support

2020-10-01 Thread Serge Semin
 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.se...@baikalelectronics.ru/

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

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

Changelog v3:
- Remove dw_spi_update_cr0() callback assignment from the DW APB SSI PCI
  glue-driver.

Signed-off-by: Serge Semin 
Cc: Alexey Malahov 
Cc: Ramil Zaripov 
Cc: Pavel Parkhomenko 
Cc: Andy Shevchenko 
Cc: Andy Shevchenko 
Cc: Lars Povlsen 
Cc: wuxu.wu 
Cc: Feng Tang 
Cc: Rob Herring 
Cc: linux-...@vger.kernel.org
Cc: devicet...@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 dev

[PATCH v3 08/21] spi: dw: Refactor data IO procedure

2020-10-01 Thread Serge Semin
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 
---
 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 92c26b02269b..4baf72b121c2 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 --

[PATCH v3 04/21] spi: dw: Update SPI bus speed in a config function

2020-10-01 Thread Serge Semin
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 
---
 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 6b89330708bc..77dfd6681f0c 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -269,8 +269,8 @@ static u32 dw_spi_get_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



[PATCH v3 02/21] spi: dw: Add DWC SSI capability

2020-10-01 Thread Serge Semin
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 

---

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 

[PATCH v3 03/21] spi: dw: Detach SPI device specific CR0 config method

2020-10-01 Thread Serge Semin
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 
---
 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..6b89330708bc 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_get_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_get_cr0(dws, spi);
+
chip->tmode = SPI_TMOD_TR;
 
return 0;
-- 
2.27.0



[PATCH v3 20/21] dt-bindings: spi: dw: Add Baikal-T1 SPI Controllers

2020-10-01 Thread Serge Semin
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 
Signed-off-by: Ramil Zaripov 
Signed-off-by: Serge Semin 
Reviewed-by: Rob Herring 
---
 .../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 = < 13 0>,
  < 14 0>;
 };
+  - |
+spi@1f040100 {
+  compatible = "baikal,bt1-sys-ssi";
+  reg = <0x1f040100 0x900>,
+<0x1c00 0x100>;
+  #address-cells = <1>;
+  #size-cells = <0>;
+  mux-controls = <_mux>;
+  clocks = <_sys>;
+  clock-names = "ssi_clk";
+};
 ...
-- 
2.27.0



[PATCH v3 19/21] spi: dw: Add poll-based SPI transfers support

2020-10-01 Thread Serge Semin
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 
---
 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 cc217b0e588e..a3c0be6943f3 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(, 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);
 
@@ -816,7 +854,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



[PATCH v3 17/21] spi: dw: Add memory operations support

2020-10-01 Thread Serge Semin
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 
Signed-off-by: Ramil Zaripov 
Signed-off-by: Serge Semin 
---
 drivers/spi/Kconfig   |   1 +
 drivers/spi/spi-dw-core.c | 300 ++
 drivers/spi/spi-dw.h  |  13 ++
 3 files changed, 314 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 72b205dc6c81..d1e8438433b8 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -8,10 +8,13 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 
 #include "spi-dw.h"
@@ -422,6 +425,300 @@ 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([i], op->data.buf.out, op->dat

[PATCH v3 18/21] spi: dw: Introduce max mem-ops SPI bus frequency setting

2020-10-01 Thread Serge Semin
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 
---
 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 d1e8438433b8..cc217b0e588e 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;
@@ -716,6 +716,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



[PATCH v3 01/21] spi: dw: Use an explicit set_cs assignment

2020-10-01 Thread Serge Semin
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 

---

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",
 >def_rx_sample_dly_ns);
-- 
2.27.0



Re: [PATCH v2 02/21] spi: dw: Add DWC SSI capability

2020-10-01 Thread Serge Semin
On Thu, Oct 01, 2020 at 10:51:05PM +0100, Mark Brown wrote:
> On Wed, Sep 30, 2020 at 09:55:26PM +0300, Serge Semin wrote:
> > 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
> 

> This doesn't build with current code in an x86 defconfig, please check
> and resend (looks like you forgot to update dw-pci):
> 
> mnt/kernel/drivers/spi/spi-dw-pci.c: In function 'spi_mid_init':
> /mnt/kernel/drivers/spi/spi-dw-pci.c:52:5: error: 'struct dw_spi' has no 
> member named 'update_cr0'
>   dws->update_cr0 = dw_spi_update_cr0;
>  ^~
> /mnt/kernel/drivers/spi/spi-dw-pci.c:52:20: error: 'dw_spi_update_cr0' 
> undeclared (first use in this function); did you mean 'dw_spi_set_cs'?
>   dws->update_cr0 = dw_spi_update_cr0;
> ^
> dw_spi_set_cs
> /mnt/kernel/drivers/spi/spi-dw-pci.c:52:20: note: each undeclared identifier 
> is reported only once for each function it appears in
> /mnt/kernel/drivers/spi/spi-dw-pci.c: In function 'spi_generic_init':
> /mnt/kernel/drivers/spi/spi-dw-pci.c:62:5: error: 'struct dw_spi' has no 
> member named 'update_cr0'
>   dws->update_cr0 = dw_spi_update_cr0;
>  ^~
> /mnt/kernel/drivers/spi/spi-dw-pci.c:62:20: error: 'dw_spi_update_cr0' 
> undeclared (first use in this function); did you mean 'dw_spi_set_cs'?
>   dws->update_cr0 = dw_spi_update_cr0;
> ^
> dw_spi_set_cs
> make[3]: *** [/mnt/kernel/scripts/Makefile.build:283: 
> drivers/spi/spi-dw-pci.o] Error 1
> make[2]: *** [/mnt/kernel/scripts/Makefile.build:500: drivers/spi] Error 2
> make[2]: *** Waiting for unfinished jobs
> make[1]: *** [/mnt/kernel/Makefile:1788: drivers] Error 2
> make[1]: *** Waiting for unfinished jobs
> make: *** [Makefile:185: __sub-make] Error 2

Oh, thanks for noticing this. I'll fix it straight away and resend. Sorry for
the inconvenience.

-Sergey


[PATCH v2 09/21] spi: dw: Refactor IRQ-based SPI transfer procedure

2020-09-30 Thread Serge Semin
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 
---
 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 4baf72b121c2..74e8f0da2883 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



[PATCH v2 13/21] spi: dw: De-assert chip-select on reset

2020-09-30 Thread Serge Semin
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 
---
 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



[PATCH v2 05/21] spi: dw: Simplify the SPI bus speed config procedure

2020-09-30 Thread Serge Semin
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 
---
 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 77dfd6681f0c..478262fb4f8e 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



[PATCH v2 16/21] spi: dw: Add generic DW SSI status-check method

2020-09-30 Thread Serge Semin
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 
---
 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 a6f86314567f..72b205dc6c81 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(>master->dev, "RX FIFO overflow detected\n");
+   ret = -EIO;
+   }
+
+   if (irq_status & SPI_INT_RXUI) {
+   dev_err(>master->dev, "RX FIFO underflow detected\n");
+   ret = -EIO;
+   }
 
-   dev_err(>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(>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(>master->dev, "%s: FIFO overrun/underrun\n", __func__);
-   dws->master->cur_msg->status = -EIO;
complete(>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



[PATCH v2 03/21] spi: dw: Detach SPI device specific CR0 config method

2020-09-30 Thread Serge Semin
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 
---
 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..6b89330708bc 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_get_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_get_cr0(dws, spi);
+
chip->tmode = SPI_TMOD_TR;
 
return 0;
-- 
2.27.0



[PATCH v2 10/21] spi: dw: Perform IRQ setup in a dedicated function

2020-09-30 Thread Serge Semin
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 
---
 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 74e8f0da2883..db3fec4195f7 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



[PATCH v2 19/21] spi: dw: Add poll-based SPI transfers support

2020-09-30 Thread Serge Semin
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 
---
 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 cc217b0e588e..a3c0be6943f3 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(, 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);
 
@@ -816,7 +854,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



[PATCH v2 18/21] spi: dw: Introduce max mem-ops SPI bus frequency setting

2020-09-30 Thread Serge Semin
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 
---
 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 d1e8438433b8..cc217b0e588e 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;
@@ -716,6 +716,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



[PATCH v2 15/21] spi: dw: Move num-of retries parameter to the header file

2020-09-30 Thread Serge Semin
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 
---
 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_BUSY0
 #define RX_BURST_LEVEL 16
 #define TX_BUSY1
@@ -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



[PATCH v2 14/21] spi: dw: Explicitly de-assert CS on SPI transfer completion

2020-09-30 Thread Serge Semin
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 
---
 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 fca929280aab..a6f86314567f 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



[PATCH v2 21/21] spi: dw: Add Baikal-T1 SPI Controller glue driver

2020-09-30 Thread Serge Semin
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 
Signed-off-by: Ramil Zaripov 
Signed-off-by: Serge Semin 
---
 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 ..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 
+//   Serge Semin 
+//
+// Baikal-T1 DW APB SPI and System Boot SPI driver
+//
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "spi-dw.h"
+
+#define BT1_BOOT_DIRMAP0
+#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);

[PATCH v2 01/21] spi: dw: Use an explicit set_cs assignment

2020-09-30 Thread Serge Semin
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 

---

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",
 >def_rx_sample_dly_ns);
-- 
2.27.0



[PATCH v2 11/21] spi: dw: Unmask IRQs after enabling the chip

2020-09-30 Thread Serge Semin
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 
---
 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 db3fec4195f7..58a7c7465c61 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



[PATCH v2 02/21] spi: dw: Add DWC SSI capability

2020-09-30 Thread Serge Semin
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 
---
 drivers/spi/spi-dw-core.c | 80 ++-
 drivers/spi/spi-dw-mmio.c | 20 +-
 drivers/spi/spi-dw.h  |  9 +
 3 files changed, 40 insertions(+), 69 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_

[PATCH v2 06/21] spi: dw: Update Rx sample delay in the config function

2020-09-30 Thread Serge Semin
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 
---
 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 478262fb4f8e..87c8f0028a23 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



[PATCH v2 12/21] spi: dw: Discard chip enabling on DMA setup error

2020-09-30 Thread Serge Semin
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 
---
 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 58a7c7465c61..fca929280aab 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



[PATCH v2 00/21] spi: dw: Add full Baikal-T1 SPI Controllers support

2020-09-30 Thread Serge Semin
 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.se...@baikalelectronics.ru/

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

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

Signed-off-by: Serge Semin 
Cc: Alexey Malahov 
Cc: Ramil Zaripov 
Cc: Pavel Parkhomenko 
Cc: Andy Shevchenko 
Cc: Andy Shevchenko 
Cc: Lars Povlsen 
Cc: wuxu.wu 
Cc: Feng Tang 
Cc: Rob Herring 
Cc: linux-...@vger.kernel.org
Cc: devicet...@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: Sim

[PATCH v2 17/21] spi: dw: Add memory operations support

2020-09-30 Thread Serge Semin
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 
Signed-off-by: Ramil Zaripov 
Signed-off-by: Serge Semin 
---
 drivers/spi/Kconfig   |   1 +
 drivers/spi/spi-dw-core.c | 300 ++
 drivers/spi/spi-dw.h  |  13 ++
 3 files changed, 314 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 72b205dc6c81..d1e8438433b8 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -8,10 +8,13 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 
 #include "spi-dw.h"
@@ -422,6 +425,300 @@ 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([i], op->data.buf.out, op->dat

[PATCH v2 20/21] dt-bindings: spi: dw: Add Baikal-T1 SPI Controllers

2020-09-30 Thread Serge Semin
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 
Signed-off-by: Ramil Zaripov 
Signed-off-by: Serge Semin 
Reviewed-by: Rob Herring 
---
 .../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 = < 13 0>,
  < 14 0>;
 };
+  - |
+spi@1f040100 {
+  compatible = "baikal,bt1-sys-ssi";
+  reg = <0x1f040100 0x900>,
+<0x1c00 0x100>;
+  #address-cells = <1>;
+  #size-cells = <0>;
+  mux-controls = <_mux>;
+  clocks = <_sys>;
+  clock-names = "ssi_clk";
+};
 ...
-- 
2.27.0



[PATCH v2 08/21] spi: dw: Refactor data IO procedure

2020-09-30 Thread Serge Semin
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 
---
 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 92c26b02269b..4baf72b121c2 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 --

[PATCH v2 04/21] spi: dw: Update SPI bus speed in a config function

2020-09-30 Thread Serge Semin
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 
---
 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 6b89330708bc..77dfd6681f0c 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -269,8 +269,8 @@ static u32 dw_spi_get_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



[PATCH v2 07/21] spi: dw: Add DW SPI controller config structure

2020-09-30 Thread Serge Semin
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 
---
 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 87c8f0028a23..92c26b02269b 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -20,10 +20,8 @@
 #include 
 #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_get_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, );
 
transfer->effective_speed_hz = dws->current_freq;
 
@@ -409,8 +416,6 @@ static int dw_spi_setup(struct spi_device *spi)
 */
chip->cr0 = dw_spi_get_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);
 exte

Re: [PATCH 11/30] spi: dw: Add DWC SSI capability

2020-09-30 Thread Serge Semin
On Wed, Sep 30, 2020 at 04:41:49PM +0100, Mark Brown wrote:
> On Wed, Sep 30, 2020 at 06:03:12PM +0300, Serge Semin wrote:
> > On Wed, Sep 30, 2020 at 01:17:37AM +0300, Serge Semin wrote:
> 
> > > > > - /*
> > > > > -  * 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;
> 
> > anyway. If you are agree with me having that done here, then please, accept 
> > the
> > patch the way it is. If you disagree, or have any other though, please give 
> > me
> > your answer, why.
> 
> Those comments did seem to help mitigate the wall of acronym soup issue
> that the code has, it seems a shame to drop them.

I see your point, but still don't think that those comment give much help like 
you
said, because the mode->register mapping can be easily derived from the macro
naming and values.

Anyway since you insist on having the comments left here, I'll get them back and
add the similar ones for the standard DW-APB-SSI version of the controller so
the code would look coherent.

-Sergey


Re: [PATCH 02/30] spi: dw: Use ternary op to init set_cs callback

2020-09-30 Thread Serge Semin
On Wed, Sep 30, 2020 at 04:01:17PM +0100, Mark Brown wrote:
> On Wed, Sep 30, 2020 at 05:57:59PM +0300, Serge Semin wrote:
> > On Wed, Sep 30, 2020 at 12:55:55AM +0300, Serge Semin wrote:
> 
> > > + if (dws->set_cs)
> > > + master->set_cs = dws->set_cs;
> > > + else
> > > + master->set_cs = dw_spi_set_cs;
> 

> > Judging by having your comment on this patch you obviously didn't like the
> > ternary operator used to assign a default value to the set_cs callback. So I
> > suggested a solution, which may suit you. What do you think about it? Agree,
> > disagree, insist on leaving this part of the code along, etc.
> 
> That looks fine.

Ok. I'll implement it in the next patchset version.

-Sergey


Re: [PATCH 11/30] spi: dw: Add DWC SSI capability

2020-09-30 Thread Serge Semin
Mark,
A concrete question is below of my previous comment.

On Wed, Sep 30, 2020 at 01:17:37AM +0300, Serge Semin wrote:
> On Tue, Sep 29, 2020 at 02:52:33PM +0100, Mark Brown wrote:
> > On Sun, Sep 20, 2020 at 02:28:55PM +0300, Serge Semin wrote:
> > 
> > > - /*
> > > -  * 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;
> > 
> 
> > > + cr0 |= SSI_MOTO_SPI << DWC_SSI_CTRLR0_FRF_OFFSET;
> > > + cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << 
> > > DWC_SSI_CTRLR0_SCPOL_OFFSET;
> > > + cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << 
> > > DWC_SSI_CTRLR0_SCPH_OFFSET;
> > 
> > The new code seems less well commented than the old code here.
> 
> You are right. The comments are omitted. The thing is that they are absolutely
> redundant here, for the same reason they haven't been added to the standard
> update_cr0() method. Both the DWC SSI-capable and standard DW APB SSI-specific
> part of the code do the same thing: setup the CTRLR0 fields, which are 
> described
> by the macro definitions. So there is no need to duplicate that information in
> the comments, moreover seeing it can be inferred from the code.
> 
> -Sergey

My response to your comment was that those in-code comments have been absolutely
redundant. So I just removed them, since I was touching that part of the driver
anyway. If you are agree with me having that done here, then please, accept the
patch the way it is. If you disagree, or have any other though, please give me
your answer, why.

-Sergey


Re: [PATCH 02/30] spi: dw: Use ternary op to init set_cs callback

2020-09-30 Thread Serge Semin
Mark,
A concrete question is below the main text.)

On Wed, Sep 30, 2020 at 12:55:55AM +0300, Serge Semin wrote:
> On Tue, Sep 29, 2020 at 02:11:53PM +0100, Mark Brown wrote:
> > On Sun, Sep 20, 2020 at 02:28:46PM +0300, Serge Semin wrote:
> > > Simplify the dw_spi_add_host() method a bit by replacing the set_cs
> > > callback overwrite procedure with direct setting the callback if a custom
> > > version of one is specified.
> > 
> > > - master->set_cs = dw_spi_set_cs;
> > > + master->set_cs = dws->set_cs ?: dw_spi_set_cs;
> > 
> > > - if (dws->set_cs)
> > > - master->set_cs = dws->set_cs;
> > 
> 

> > This doesn't look like a win for legibility or comprehensibility.
> 
> Assigning a default value and redefining it way later doesn't look legible
> either, because in that case you'd need to keep in mind, that some callback 
> has
> already been set. Moreover it does one redundant assignment. That's why I
> decided to implement the setting up by means of the ternary op.
> 
> If you don't like the ternary op, then we could use an explicit if-else
> statement here. But I'd insist on implementing the assignment in a one
> place of the function instead of having it partly perform here and partly 
> there.
> Like this:
> 
> --- 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;
> 
> Personally I prefer the ternary op in such situations. The operator provides 
> an
> elegant small well known solution for the default-assignments. I don't see it
> as non-legible or incomprehensible. (I don't really understand why you and
> Andy don't like the operator that much =))
> 
> -Sergey

Judging by having your comment on this patch you obviously didn't like the
ternary operator used to assign a default value to the set_cs callback. So I
suggested a solution, which may suit you. What do you think about it? Agree,
disagree, insist on leaving this part of the code along, etc.

-Sergey


Re: [PATCH 0/2] mips: Introduce some IO-accessors optimizations

2020-09-30 Thread Serge Semin
On Wed, Sep 30, 2020 at 12:15:32PM +0200, Thomas Bogendoerfer wrote:
> On Wed, Sep 30, 2020 at 12:12:32AM +0300, Serge Semin wrote:
> > Thomas,
> > Any comment on the series? The changes aren't that comprehensive, so it 
> > would
> > be great to merge it in before the 5.10 merge window is opened.
> 
> for the both patches there is no user for it, so I don't see a reason
> to apply it.

@Thomas. I see your point. I'll merge them into my repo with Baikal-T1 CSP/BSP
patches and will deliver all at once when the kernel is ready to accept the
changes (most likely in 3 - 5 months).

@Jiaxun, if you've any hardware which for sure supports the strong UC
ordering, feel free to submit a patchset which activates the proposed here
config together with my STRONG_UC_ORDERING-alteration applied before your
changes.

-Sergey

> 
> Thomas.
> 
> -- 
> Crap can work. Given enough thrust pigs will fly, but it's not necessarily a
> good idea.[ RFC1925, 2.3 ]


Re: [PATCH 00/30] spi: dw: Add full Baikal-T1 SPI Controllers support

2020-09-30 Thread Serge Semin
On Wed, Sep 30, 2020 at 12:04:04PM +0100, Mark Brown wrote:
> On Wed, Sep 30, 2020 at 01:43:03AM +0300, Serge Semin wrote:
> > On Tue, Sep 29, 2020 at 03:43:51PM +0100, Mark Brown wrote:
> 
> > > This is a *huge* patch series which is a bit unweildy to review
> > > (especially given the other 10+ patch series you sent at the same time),
> 
> > Yeah, sorry about the bulky series. If most of the changes have been more
> > complicated than that, less inter-dependent and less directed to having the 
> > code
> > prepared for the main alterations I would have definitely split them up in
> > different series. But the biggest part of the patchset is just a preparation
> > before adding the mem-ops, poll-based transfers and Baikal-T1 SPI support. 
> > So
> > having them submitted without the main part of the patchset would be just 
> > weird.
> 
> One option with things like this is to just not send everything at once
> - even when split into multiple series it's a huge bulk of patches in an
> inbox.  Unless the patches are obviously from their subjects repetitive
> people probably aren't getting far enough in to look at the actual
> patches or even their sizes before deciding it looks like a lot of work
> and putting things off for later.
> 
> > I see you have already merged in the first nine patches. So would you like 
> > me
> > to split the rest of them up into two series or it would be ok to resend (if
> > required) them as one series seeing it's not that bulky anymore?
> 

> Not all of the first 9, IIRC I skipped one I had comments on.

Yes, you skipped one and I've already given you my response on your comment
about it: [PATCH 02/30] spi: dw: Use ternary op to init set_cs callback
So have I responded to your comment on another patch:
[PATCH 11/30] spi: dw: Add DWC SSI capability .

I will need a response from you about them to go further with this patchset.

> If they
> can be split that would probably be helpful, if there are dependencies
> then it's not going to buy too much.

Well, all later patches depend on the changes introduced in the previous ones in
one way or another. So in any case that will be an incremental series of 
patchsets
otherwise they most likely won't get applied cleanly on the driver source code.
For now we have got 21 patch left to review:
I) First two ones you've given your comments on and are mostly related to the
patches you have already merged in.
1. 688c17cad5c2 spi: dw: Use ternary op to init set_cs callback
2. 17d0b3abc03d spi: dw: Add DWC SSI capability

II) Refactor the DW APB SSI controller config procedure.
3. 6a436c824961 spi: dw: Detach SPI device specific CR0 config method
4. 47614d60e44c spi: dw: Update SPI bus speed in a config function
5. df64a4961801 spi: dw: Simplify the SPI bus speed config procedure
6. 1a583b130bab spi: dw: Update Rx sample delay in the config function
7. 9f205a8939a2 spi: dw: Add DW SPI controller config structure

III) Refactor IRQ-based SPI transfer procedure.
8. d4fa973a3f7c spi: dw: Refactor data IO procedure
9. d998b98e3d93 spi: dw: Refactor IRQ-based SPI transfer procedure
10. 7fc419af6e67 spi: dw: Perform IRQ setup in a dedicated function
11. d3dfd997379a spi: dw: Unmask IRQs after enabling the chip
12. 6ecf589320f3 spi: dw: Discard chip enabling on DMA setup error

IV) Final preparation before adding the memory operations.
13. 84a03fad452c spi: dw: De-assert chip-select on reset
14. dd0212eb5738 spi: dw: Explicitly de-assert CS on SPI transfer completion
15. d1eea0f556cf spi: dw: Move num-of retries parameter to the header file
16. 3e70e5a6c1d9 spi: dw: Add generic DW SSI status-check method

v) Introduce memory and poll-based operations.
17. 52d733f30464 spi: dw: Add memory operations support
18. c2f45eb3d662 spi: dw: Introduce max mem-ops SPI bus frequency setting
19. ccf08869b6bd spi: dw: Add poll-based SPI transfers support

vI) Add Baikal-T1 glue-driver
20. a536c408f7aa dt-bindings: spi: dw: Add Baikal-T1 SPI Controllers
21. 791e68755ead spi: dw: Add Baikal-T1 SPI Controller glue driver

If you want I can resend the series split up as I described above. Alternatively
I can collect I) - III) into a one patchset and IV) - VI) into another one.
So to speak I'll do in whatever scenario you prefer. Just tell me which one is
more suitable for you to review.

In anyway we need to settle the issues regarding the first two patches. Please 
give
me your answers on the comments I've left there in response to your comments.)

-Sergey


Re: [PATCH 00/30] spi: dw: Add full Baikal-T1 SPI Controllers support

2020-09-29 Thread Serge Semin
Hi Mark

On Tue, Sep 29, 2020 at 03:43:51PM +0100, Mark Brown wrote:
> On Sun, Sep 20, 2020 at 02:28:44PM +0300, Serge Semin wrote:
> 
> > 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
> 

> This is a *huge* patch series which is a bit unweildy to review
> (especially given the other 10+ patch series you sent at the same time),

Yeah, sorry about the bulky series. If most of the changes have been more
complicated than that, less inter-dependent and less directed to having the code
prepared for the main alterations I would have definitely split them up in
different series. But the biggest part of the patchset is just a preparation
before adding the mem-ops, poll-based transfers and Baikal-T1 SPI support. So
having them submitted without the main part of the patchset would be just weird.

The other 10+ patches were sent months ago. I've just resent them with minor
alterations to get more attention.) Anyway since they concern an absolutely
different functionality (DW APB SSI DMA driver) of course I've delivered them in
the framework of the different patchset.

> once you start getting over 10 patches it's time to pay attention to
> series length and the fact that you're outlining a bunch of tangentially
> related areas which could have been split out easily enough.  It is much
> better to send smaller sets of patches at once, or if you're sending a
> lot then to split them into smaller serieses.  This will tend to make
> the review more approachable which will in turn tend to make things go
> faster, people are much more likely to put off going through a huge
> series.

I see you have already merged in the first nine patches. So would you like me
to split the rest of them up into two series or it would be ok to resend (if
required) them as one series seeing it's not that bulky anymore?

-Sergey


Re: [PATCH 11/30] spi: dw: Add DWC SSI capability

2020-09-29 Thread Serge Semin
On Tue, Sep 29, 2020 at 02:52:33PM +0100, Mark Brown wrote:
> On Sun, Sep 20, 2020 at 02:28:55PM +0300, Serge Semin wrote:
> 
> > -   /*
> > -* 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;
> 

> > +   cr0 |= SSI_MOTO_SPI << DWC_SSI_CTRLR0_FRF_OFFSET;
> > +   cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << 
> > DWC_SSI_CTRLR0_SCPOL_OFFSET;
> > +   cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << 
> > DWC_SSI_CTRLR0_SCPH_OFFSET;
> 
> The new code seems less well commented than the old code here.

You are right. The comments are omitted. The thing is that they are absolutely
redundant here, for the same reason they haven't been added to the standard
update_cr0() method. Both the DWC SSI-capable and standard DW APB SSI-specific
part of the code do the same thing: setup the CTRLR0 fields, which are described
by the macro definitions. So there is no need to duplicate that information in
the comments, moreover seeing it can be inferred from the code.

-Sergey


Re: [PATCH 04/30] Revert: spi: spi-dw: Add lock protect dw_spi rx/tx to prevent concurrent calls

2020-09-29 Thread Serge Semin
On Tue, Sep 29, 2020 at 02:28:11PM +0100, Mark Brown wrote:
> On Sun, Sep 20, 2020 at 02:28:48PM +0300, Serge Semin wrote:
> > There is no point in having the commit 19b61392c5a8 ("spi: spi-dw: Add
> > lock protect dw_spi rx/tx to prevent concurrent calls") applied. The
> > commit author made an assumption that the problem with the rx data
> 

> Please submit patches using subject lines reflecting the style for the
> subsystem, this makes it easier for people to identify relevant patches.
> Look at what existing commits in the area you're changing are doing and
> make sure your subject lines visually resemble what they're doing.
> There's no need to resubmit to fix this alone.

Ok. Thank you for pointing that out. I'll do like you said next time on a
patch reversion.

-Sergey


Re: [PATCH 03/30] spi: dw: Initialize n_bytes before the memory barrier

2020-09-29 Thread Serge Semin
On Tue, Sep 29, 2020 at 02:12:25PM +0100, Mark Brown wrote:
> On Sun, Sep 20, 2020 at 02:28:47PM +0300, Serge Semin wrote:
> > Since n_bytes field of the DW SPI private data is also utilized by the
> > IRQ handler, we need to make sure it' initialization is done before the
> > memory barrier.
> 

> This looks like a fix so should have been before any cosmetic cleanups.

Ah, sorry about that. I had that in mind, but have just forgotten to move it
to the series head.

-Sergey


Re: [PATCH 02/30] spi: dw: Use ternary op to init set_cs callback

2020-09-29 Thread Serge Semin
On Tue, Sep 29, 2020 at 02:11:53PM +0100, Mark Brown wrote:
> On Sun, Sep 20, 2020 at 02:28:46PM +0300, Serge Semin wrote:
> > Simplify the dw_spi_add_host() method a bit by replacing the set_cs
> > callback overwrite procedure with direct setting the callback if a custom
> > version of one is specified.
> 
> > -   master->set_cs = dw_spi_set_cs;
> > +   master->set_cs = dws->set_cs ?: dw_spi_set_cs;
> 
> > -   if (dws->set_cs)
> > -   master->set_cs = dws->set_cs;
> 

> This doesn't look like a win for legibility or comprehensibility.

Assigning a default value and redefining it way later doesn't look legible
either, because in that case you'd need to keep in mind, that some callback has
already been set. Moreover it does one redundant assignment. That's why I
decided to implement the setting up by means of the ternary op.

If you don't like the ternary op, then we could use an explicit if-else
statement here. But I'd insist on implementing the assignment in a one
place of the function instead of having it partly perform here and partly there.
Like this:

--- 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;

Personally I prefer the ternary op in such situations. The operator provides an
elegant small well known solution for the default-assignments. I don't see it
as non-legible or incomprehensible. (I don't really understand why you and
Andy don't like the operator that much =))

-Sergey


Re: [PATCH 0/2] mips: Introduce some IO-accessors optimizations

2020-09-29 Thread Serge Semin
Thomas,
Any comment on the series? The changes aren't that comprehensive, so it would
be great to merge it in before the 5.10 merge window is opened.

-Sergey

On Sun, Sep 20, 2020 at 02:00:08PM +0300, Serge Semin wrote:
> It has been discovered that on our MIPS P5600-based CPU the IO accessors
> aren't that rapid as they could be even taking into account a relatively
> slow AXI2APB bridge embedded into the system interconnect. Turned out we
> can introduce two types of optimizations. First we can remove the
> execution barriers from the relaxed IO-accessors as our CPU conforms to
> the MIPS Coherency Protocol Specification [1, 2]. Of course it also
> concerns the IO interconnect implementation. So in accordance with [3] we
> suggest to remove the barriers at least for the platforms which conform
> the specification the same way as ours. Second there is a dedicated
> Coherency Manager control register, which can be also used to tune the IO
> methods up. For some reason it hasn't been added to the MIPS arch code so
> far, while it provides flags for instance to speed the SYNC barrier for
> the platforms with non-re-ordering IO interconnect, to set the cache ops
> serialization limits, enable the speculative reads, etc. For now we
> suggest to add just the macro with the CM2 GCR_CONTROL register accessors
> and fields description. So any platform could use it to activate the
> corresponding optimization. Our platform-wise we'll do this in the
> framework of our Baikal-T1 platform code in the prom_init() method.
> 
> [1] MIPS Coherence Protocol Specification, Document Number: MD00605,
> Revision 01.01. September 14, 2015, 4.2 Execution Order Behavior,
> p. 33
> 
> [2] MIPS Coherence Protocol Specification, Document Number: MD00605,
> Revision 01.01. September 14, 2015, 4.8.1 IO Device Access, p. 58
> 
> [3] "LINUX KERNEL MEMORY BARRIERS", Documentation/memory-barriers.txt,
> Section "KERNEL I/O BARRIER EFFECTS"
> 
> Signed-off-by: Serge Semin 
> Cc: Alexey Malahov 
> Cc: Pavel Parkhomenko 
> Cc: Vadim Vlasov 
> Cc: Maciej W. Rozycki 
> Cc: linux-m...@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> 
> Serge Semin (2):
>   mips: Add strong UC ordering config
>   mips: Introduce MIPS CM2 GCR Control register accessors
> 
>  arch/mips/Kconfig   |  8 
>  arch/mips/include/asm/io.h  | 20 ++--
>  arch/mips/include/asm/mips-cm.h | 15 +++
>  3 files changed, 33 insertions(+), 10 deletions(-)
> 
> -- 
> 2.27.0
> 


Re: [PATCH 0/3] serial: 8250_dw: Fix clk-notifier/port suspend deadlock

2020-09-29 Thread Serge Semin
Hello,

On Sun, Sep 27, 2020 at 05:01:52PM +0200, Hans de Goede wrote:
> Hi,
> 
> On 9/23/20 6:19 PM, Serge Semin wrote:
> > Hans has discovered that there is a potential deadlock between the ref
> > clock change notifier and the port suspension procedures {see the link at
> > the bottom of the letter}. Indeed the deadlock is possible if the port
> > suspension is initiated during the ref clock rate change:
> > 
> >  CPU0 (suspend CPU/UART)   CPU1 (update clock)
> >   
> >  lock(>mutex);
> >lock((work_completion)(>clk_work));
> >lock(>mutex);
> >  lock((work_completion)(>clk_work));
> > 
> >  *** DEADLOCK ***
> > 
> > So the CPU performing the UART port shutdown procedure will wait until the
> > ref clock change notifier is finished (worker is flushed), while the later
> > will wait for a port mutex being released.
> > 
> > A possible solution to bypass the deadlock is to move the worker flush out
> > of the critical section protected by the TTY port mutex. For instance we
> > can register and de-register the clock change notifier in the port probe
> > and remove methods instead of having them called from the port
> > startup/shutdown callbacks. But in order to do that we need to make sure
> > that the serial8250_update_uartclk() method is safe to be used while the
> > port is shutted down. Alas the current implementation doesn't provide that
> > safety. The solution described above is introduced in the framework of
> > this patchset. See individual patches for details.
> > 
> > Link: 
> > https://lore.kernel.org/linux-serial/f1cd5c75-9cda-6896-a4e2-42c5bfc3f...@redhat.com
> > 
> > Hans, could you test the patchset out on your Cherry Trail (x86)-based
> > devices? After that we can merge it in into the kernels 5.8 and 5.9 if
> > there is no objections against the fix.
> 
> Done, I can confirm that this fixes the lockdep issue for me, so you
> can add my:
> 
> Tested-by: Hans de Goede 

Great! Thank you very much.

Greg, could you merge the series in if you have no objection against the
solution design? Seeing the bug has been introduced together with the
original series integrated in the kernel 5.9, the fix provided by this
patchset will be only needed in 5.9.

-Sergey

> 
> To the entire series.
> 
> Regards,
> 
> Hans
> 


Re: [PATCH 1/2] mips: Add strong UC ordering config

2020-09-25 Thread Serge Semin
On Fri, Sep 25, 2020 at 11:54:20AM +0800, Jiaxun Yang wrote:
> 
> 
> 在 2020/9/20 19:00, Serge Semin 写道:
> > In accordance with [1, 2] memory transactions using CCA=2 (Uncached
> > Cacheability and Coherency Attribute) are always strongly ordered. This
> > means the younger memory accesses using CCA=2 are never allowed to be
> > executed before older memory accesses using CCA=2 (no bypassing is
> > allowed), and Loads and Stores using CCA=2 are never speculative. It is
> > expected by the specification that the rest of the system maintains these
> > properties for processor initiated uncached accesses. So the system IO
> > interconnect doesn't reorder uncached transactions once they have left the
> > processor subsystem. Taking into account these properties and what [3]
> > says about the relaxed IO-accessors we can infer that normal Loads and
> > Stores from/to CCA=2 memory and without any additional execution barriers
> > will fully comply with the {read,write}X_relaxed() methods requirements.
> > 
> > Let's convert then currently generated relaxed IO-accessors to being pure
> > Loads and Stores. Seeing the commit 3d474dacae72 ("MIPS: Enforce strong
> > ordering for MMIO accessors") and commit 8b656253a7a4 ("MIPS: Provide
> > actually relaxed MMIO accessors") have already made a preparation in the
> > corresponding macro, we can do that just by replacing the "barrier"
> > parameter utilization with the "relax" one. Note the "barrier" macro
> > argument can be removed, since it isn't fully used anyway other than being
> > always assigned to 1.
> > 
> > Of course it would be fullish to believe that all the available MIPS-based
> > CPUs completely follow the denoted specification, especially considering
> > how old the architecture is. Instead we introduced a dedicated kernel
> > config, which when enabled will convert the relaxed IO-accessors to being
> > pure Loads and Stores without any additional barriers around. So if some
> > CPU supports the strongly ordered UC memory access, it can enable that
> > config and use a fully optimized relaxed IO-methods. For instance,
> > Baikal-T1 architecture support code will do that.
> > 
> > [1] MIPS Coherence Protocol Specification, Document Number: MD00605,
> >  Revision 01.01. September 14, 2015, 4.2 Execution Order Behavior,
> >  p. 33
> > 
> > [2] MIPS Coherence Protocol Specification, Document Number: MD00605,
> >  Revision 01.01. September 14, 2015, 4.8.1 IO Device Access, p. 58
> > 
> > [3] "LINUX KERNEL MEMORY BARRIERS", Documentation/memory-barriers.txt,
> >  Section "KERNEL I/O BARRIER EFFECTS"
> > 
> > Signed-off-by: Serge Semin 
> > Cc: Maciej W. Rozycki 

> Reviewed-by: Jiaxun Yang 
> 
> 
> Based on #mipslinus discussions, I suspect this option can be selected by
> most modern MIPS processors including all IMG/MTI cores,
> Ingenic and Loongson.

Thanks for reviewing the patch.

Regarding the option. Alas it's not that easy and we must be very careful
with assumption whether some processor supports the denoted feature. Even
if the MIPS cores do imply the strict UC load/stores ordering, the system
interconnects may still perform the out-of-order requests execution. For
instance, the P5600 cores installed into our Baikal-T1 SoC do support the
strong UC ordering, but there is a cascade of the OCP2AXI, AXI2AXI and
AXI2APB bridges behind the CPU memory interface, each of which is equipped
with an internal FIFO and some complicated logic of the traffic routing.
So each platform should be carefully analyzed and tested (if it's
possible) before enabling the suggested feature, otherwise we'll risk to end
up with in general working, but at some point buggy, systems. Needless to
say, that out-of-order exec problems is very hard to track and debug due
to a random nature of impact on the system.

-Sergey

> 
> Thanks.
> 
> - Jiaxun
> 
> > ---
> >   arch/mips/Kconfig  |  8 
> >   arch/mips/include/asm/io.h | 20 ++--
> >   2 files changed, 18 insertions(+), 10 deletions(-)
> > 


[PATCH 2/3] serial: 8250: Skip uninitialized TTY port baud rate update

2020-09-23 Thread Serge Semin
It is erroneous to update the TTY port baud rate if it hasn't been
initialized yet, because in that case the TTY struct isn't set. So there
is no termios structure to get and re-calculate the baud if the current
baud can't be reached. Let's skip the baud rate update then until the port
is fully initialized.

Note the update UART clock method still sets the uartclk member with a new
ref clock value even if the port is turned off. The new UART ref clock
rate will be used later on the port starting up procedure.

Fixes: 868f3ee6e452 ("serial: 8250: Add 8250 port clock update method")
Signed-off-by: Serge Semin 
---
 drivers/tty/serial/8250/8250_port.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_port.c 
b/drivers/tty/serial/8250/8250_port.c
index 1259fb6b66b3..b0af13074cd3 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -2653,6 +2653,10 @@ void serial8250_update_uartclk(struct uart_port *port, 
unsigned int uartclk)
goto out_lock;
 
port->uartclk = uartclk;
+
+   if (!tty_port_initialized(>state->port))
+   goto out_lock;
+
termios = >state->port.tty->termios;
 
baud = serial8250_get_baud_rate(port, termios, NULL);
-- 
2.27.0



[PATCH 3/3] serial: 8250_dw: Fix clk-notifier/port suspend deadlock

2020-09-23 Thread Serge Semin
It has been discovered that there is a potential deadlock between
the clock-change-notifier thread and the UART port suspending one:

   CPU0 (suspend CPU/UART)   CPU1 (update clock)

   lock(>mutex);
 lock((work_completion)(>clk_work));
 lock(>mutex);
   lock((work_completion)(>clk_work));

   *** DEADLOCK ***

The best way to fix this is to eliminate the CPU0
port->mutex/work-completion scenario. So we suggest to register and
unregister the clock-notifier during the DW APB UART port probe/remove
procedures, instead of doing that at the points of the port
startup/shutdown.

Link: 
https://lore.kernel.org/linux-serial/f1cd5c75-9cda-6896-a4e2-42c5bfc3f...@redhat.com

Fixes: cc816969d7b5 ("serial: 8250_dw: Fix common clocks usage race condition")
Reported-by: Hans de Goede 
Signed-off-by: Serge Semin 
---
 drivers/tty/serial/8250/8250_dw.c | 54 +++
 1 file changed, 19 insertions(+), 35 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_dw.c 
b/drivers/tty/serial/8250/8250_dw.c
index 87f450b7c177..9e204f9b799a 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -373,39 +373,6 @@ static void dw8250_set_ldisc(struct uart_port *p, struct 
ktermios *termios)
serial8250_do_set_ldisc(p, termios);
 }
 
-static int dw8250_startup(struct uart_port *p)
-{
-   struct dw8250_data *d = to_dw8250_data(p->private_data);
-   int ret;
-
-   /*
-* Some platforms may provide a reference clock shared between several
-* devices. In this case before using the serial port first we have to
-* make sure that any clock state change is known to the UART port at
-* least post factum.
-*/
-   if (d->clk) {
-   ret = clk_notifier_register(d->clk, >clk_notifier);
-   if (ret)
-   dev_warn(p->dev, "Failed to set the clock notifier\n");
-   }
-
-   return serial8250_do_startup(p);
-}
-
-static void dw8250_shutdown(struct uart_port *p)
-{
-   struct dw8250_data *d = to_dw8250_data(p->private_data);
-
-   serial8250_do_shutdown(p);
-
-   if (d->clk) {
-   clk_notifier_unregister(d->clk, >clk_notifier);
-
-   flush_work(>clk_work);
-   }
-}
-
 /*
  * dw8250_fallback_dma_filter will prevent the UART from getting just any free
  * channel on platforms that have DMA engines, but don't have any channels
@@ -501,8 +468,6 @@ static int dw8250_probe(struct platform_device *pdev)
p->serial_out   = dw8250_serial_out;
p->set_ldisc= dw8250_set_ldisc;
p->set_termios  = dw8250_set_termios;
-   p->startup  = dw8250_startup;
-   p->shutdown = dw8250_shutdown;
 
p->membase = devm_ioremap(dev, regs->start, resource_size(regs));
if (!p->membase)
@@ -622,6 +587,19 @@ static int dw8250_probe(struct platform_device *pdev)
goto err_reset;
}
 
+   /*
+* Some platforms may provide a reference clock shared between several
+* devices. In this case any clock state change must be known to the
+* UART port at least post factum.
+*/
+   if (data->clk) {
+   err = clk_notifier_register(data->clk, >clk_notifier);
+   if (err)
+   dev_warn(p->dev, "Failed to set the clock notifier\n");
+   else
+   queue_work(system_unbound_wq, >clk_work);
+   }
+
platform_set_drvdata(pdev, data);
 
pm_runtime_set_active(dev);
@@ -648,6 +626,12 @@ static int dw8250_remove(struct platform_device *pdev)
 
pm_runtime_get_sync(dev);
 
+   if (data->clk) {
+   clk_notifier_unregister(data->clk, >clk_notifier);
+
+   flush_work(>clk_work);
+   }
+
serial8250_unregister_port(data->data.line);
 
reset_control_assert(data->rst);
-- 
2.27.0



[PATCH 1/3] serial: 8250: Discard RTS/DTS setting from clock update method

2020-09-23 Thread Serge Semin
It has been a mistake to add the MCR register RTS/DTS fields setting in
the generic method of the UART reference clock update. There is no point
in asserting these lines at that procedure. Just discard the
serial8250_out_MCR() mathod invocation from there then.

Fixes: 868f3ee6e452 ("serial: 8250: Add 8250 port clock update method")
Signed-off-by: Serge Semin 
---
 drivers/tty/serial/8250/8250_port.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_port.c 
b/drivers/tty/serial/8250/8250_port.c
index c71d647eb87a..1259fb6b66b3 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -2665,7 +2665,6 @@ void serial8250_update_uartclk(struct uart_port *port, 
unsigned int uartclk)
 
serial8250_set_divisor(port, baud, quot, frac);
serial_port_out(port, UART_LCR, up->lcr);
-   serial8250_out_MCR(up, UART_MCR_DTR | UART_MCR_RTS);
 
spin_unlock_irqrestore(>lock, flags);
serial8250_rpm_put(up);
-- 
2.27.0



[PATCH 0/3] serial: 8250_dw: Fix clk-notifier/port suspend deadlock

2020-09-23 Thread Serge Semin
Hans has discovered that there is a potential deadlock between the ref
clock change notifier and the port suspension procedures {see the link at
the bottom of the letter}. Indeed the deadlock is possible if the port
suspension is initiated during the ref clock rate change:

CPU0 (suspend CPU/UART)   CPU1 (update clock)
 
lock(>mutex);
  lock((work_completion)(>clk_work));
  lock(>mutex);
lock((work_completion)(>clk_work));

*** DEADLOCK ***

So the CPU performing the UART port shutdown procedure will wait until the
ref clock change notifier is finished (worker is flushed), while the later
will wait for a port mutex being released.

A possible solution to bypass the deadlock is to move the worker flush out
of the critical section protected by the TTY port mutex. For instance we
can register and de-register the clock change notifier in the port probe
and remove methods instead of having them called from the port
startup/shutdown callbacks. But in order to do that we need to make sure
that the serial8250_update_uartclk() method is safe to be used while the
port is shutted down. Alas the current implementation doesn't provide that
safety. The solution described above is introduced in the framework of
this patchset. See individual patches for details.

Link: 
https://lore.kernel.org/linux-serial/f1cd5c75-9cda-6896-a4e2-42c5bfc3f...@redhat.com

Hans, could you test the patchset out on your Cherry Trail (x86)-based
devices? After that we can merge it in into the kernels 5.8 and 5.9 if
there is no objections against the fix.

Note, in order to have the fix working for the older kernel all of patches
need to be backported.

Fixes: cc816969d7b5 ("serial: 8250_dw: Fix common clocks usage race condition")
Fixes: 868f3ee6e452 ("serial: 8250: Add 8250 port clock update method")
Reported-by: Hans de Goede 
Signed-off-by: Serge Semin 
Cc: Alexey Malahov 
Cc: Pavel Parkhomenko 
Cc: Andy Shevchenko 
Cc: Maxime Ripard 
Cc: Will Deacon 
Cc: Russell King 
Cc: linux-arm-ker...@lists.infradead.org
Cc: linux-ser...@vger.kernel.org
Cc: linux-kernel@vger.kernel.org

Serge Semin (3):
  serial: 8250: Discard RTS/DTS setting from clock update method
  serial: 8250: Skip uninitialized TTY port baud rate update
  serial: 8250_dw: Fix clk-notifier/port suspend deadlock

 drivers/tty/serial/8250/8250_dw.c   | 54 ++---
 drivers/tty/serial/8250/8250_port.c |  5 ++-
 2 files changed, 23 insertions(+), 36 deletions(-)

-- 
2.27.0



[PATCH 30/30] spi: dw: Add Baikal-T1 SPI Controller glue driver

2020-09-20 Thread Serge Semin
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 
Signed-off-by: Ramil Zaripov 
Signed-off-by: Serge Semin 
---
 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 ..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 
+//   Serge Semin 
+//
+// Baikal-T1 DW APB SPI and System Boot SPI driver
+//
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "spi-dw.h"
+
+#define BT1_BOOT_DIRMAP0
+#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);

[PATCH 06/30] spi: dw: Disable all IRQs when controller is unused

2020-09-20 Thread Serge Semin
It's a good practice to disable all IRQs if a device is fully unused. In
our case it is supposed to be done before requesting the IRQ and after the
last byte of an SPI transfer is received. In the former case it's required
to prevent the IRQ handler invocation before the driver data is fully
initialized (which may happen if the IRQs status has been left uncleared
before the device is probed). So we just moved the spi_hw_init() method
invocation to the earlier stage before requesting the IRQ. In the later
case there is just no point in having any of the IRQs enabled between SPI
transfers and when there is no SPI message currently being processed.

Signed-off-by: Serge Semin 
---
 drivers/spi/spi-dw-core.c | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 18411f5b9954..be94ed5bb896 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -198,7 +198,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
 
dw_reader(dws);
if (dws->rx_end == dws->rx) {
-   spi_mask_intr(dws, SPI_INT_TXEI);
+   spi_mask_intr(dws, 0xff);
spi_finalize_current_transfer(dws->master);
return IRQ_HANDLED;
}
@@ -222,7 +222,7 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
return IRQ_NONE;
 
if (!master->cur_msg) {
-   spi_mask_intr(dws, SPI_INT_TXEI);
+   spi_mask_intr(dws, 0xff);
return IRQ_HANDLED;
}
 
@@ -458,6 +458,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
 
spi_controller_set_devdata(master, dws);
 
+   /* Basic HW init */
+   spi_hw_init(dev, dws);
+
ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev),
  master);
if (ret < 0) {
@@ -485,9 +488,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
device_property_read_u32(dev, "rx-sample-delay-ns",
 >def_rx_sample_dly_ns);
 
-   /* Basic HW init */
-   spi_hw_init(dev, dws);
-
if (dws->dma_ops && dws->dma_ops->dma_init) {
ret = dws->dma_ops->dma_init(dev, dws);
if (ret) {
-- 
2.27.0



[PATCH 25/30] spi: dw: Add generic DW SSI status-check method

2020-09-20 Thread Serge Semin
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 
---
 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 7a25ea6f4af6..77d61ded9256 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(>master->dev, "RX FIFO overflow detected\n");
+   ret = -EIO;
+   }
+
+   if (irq_status & SPI_INT_RXUI) {
+   dev_err(>master->dev, "RX FIFO underflow detected\n");
+   ret = -EIO;
+   }
 
-   dev_err(>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(>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(>master->dev, "%s: FIFO overrun/underrun\n", __func__);
-   dws->master->cur_msg->status = -EIO;
complete(>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



[PATCH 15/30] spi: dw: Update Rx sample delay in the config function

2020-09-20 Thread Serge Semin
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 
---
 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 92138a6ada12..f00fc4828480 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -273,13 +273,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;
@@ -305,12 +310,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



[PATCH 21/30] spi: dw: Discard chip enabling on DMA setup error

2020-09-20 Thread Serge Semin
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 
---
 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 8dbe11c1821c..65db4dd3ea8a 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -351,10 +351,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



[PATCH 20/30] spi: dw: Unmask IRQs after enabling the chip

2020-09-20 Thread Serge Semin
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 
---
 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 08bc53b9de88..8dbe11c1821c 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -355,8 +355,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);
@@ -364,6 +362,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



[PATCH 04/30] Revert: spi: spi-dw: Add lock protect dw_spi rx/tx to prevent concurrent calls

2020-09-20 Thread Serge Semin
There is no point in having the commit 19b61392c5a8 ("spi: spi-dw: Add
lock protect dw_spi rx/tx to prevent concurrent calls") applied. The
commit author made an assumption that the problem with the rx data
mismatch was due to the lack of the data protection. While most likely it
was caused by the lack of the memory barrier. So having the
commit bfda044533b2 ("spi: dw: use "smp_mb()" to avoid sending spi data
error") applied would be enough to fix the problem.

Indeed the spin unlock operation makes sure each memory operation issued
before the release will be completed before it's completed. In other words
it works as an implicit one way memory barrier. So having both smp_mb()
and the spin_unlock_irqrestore() here is just redundant. One of them would
be enough. It's better to leave the smp_mb() since the Tx/Rx buffers
consistency is provided by the data transfer algorithm implementation:
first we initialize the buffers pointers, then make sure the assignments
are visible by the other CPUs by calling the smp_mb(), only after that
enable the interrupt, which handler uses the buffers.

Signed-off-by: Serge Semin 

---

Folks. I have also a doubt whether the SMP memory barrier is required there
because the normal IO-methods like readl/writel imply a full memory barrier. So
any memory operation performed before them are supposed to be seen by devices
and another CPUs [1]. So most likely there could have been a problem with those
IOs implementation on the subject platform or a spurious interrupt could
have been raised during the data initialization. What do you think?
Am I missing something?

[1] "LINUX KERNEL MEMORY BARRIERS", Documentation/memory-barriers.txt,
Section "KERNEL I/O BARRIER EFFECTS"
---
 drivers/spi/spi-dw-core.c | 14 ++
 drivers/spi/spi-dw.h  |  1 -
 2 files changed, 2 insertions(+), 13 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 1af74362461d..18411f5b9954 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -142,11 +142,9 @@ static inline u32 rx_max(struct dw_spi *dws)
 
 static void dw_writer(struct dw_spi *dws)
 {
-   u32 max;
+   u32 max = tx_max(dws);
u16 txw = 0;
 
-   spin_lock(>buf_lock);
-   max = tx_max(dws);
while (max--) {
/* Set the tx word if the transfer's original "tx" is not null 
*/
if (dws->tx_end - dws->len) {
@@ -158,16 +156,13 @@ static void dw_writer(struct dw_spi *dws)
dw_write_io_reg(dws, DW_SPI_DR, txw);
dws->tx += dws->n_bytes;
}
-   spin_unlock(>buf_lock);
 }
 
 static void dw_reader(struct dw_spi *dws)
 {
-   u32 max;
+   u32 max = rx_max(dws);
u16 rxw;
 
-   spin_lock(>buf_lock);
-   max = rx_max(dws);
while (max--) {
rxw = dw_read_io_reg(dws, DW_SPI_DR);
/* Care rx only if the transfer's original "rx" is not null */
@@ -179,7 +174,6 @@ static void dw_reader(struct dw_spi *dws)
}
dws->rx += dws->n_bytes;
}
-   spin_unlock(>buf_lock);
 }
 
 static void int_error_stop(struct dw_spi *dws, const char *msg)
@@ -291,21 +285,18 @@ static int dw_spi_transfer_one(struct spi_controller 
*master,
 {
struct dw_spi *dws = spi_controller_get_devdata(master);
struct chip_data *chip = spi_get_ctldata(spi);
-   unsigned long flags;
u8 imask = 0;
u16 txlevel = 0;
u32 cr0;
int ret;
 
dws->dma_mapped = 0;
-   spin_lock_irqsave(>buf_lock, flags);
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->rx = transfer->rx_buf;
dws->rx_end = dws->rx + transfer->len;
dws->len = transfer->len;
-   spin_unlock_irqrestore(>buf_lock, flags);
 
/* Ensure dw->rx and dw->rx_end are visible */
smp_mb();
@@ -464,7 +455,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
dws->master = master;
dws->type = SSI_MOTO_SPI;
dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR);
-   spin_lock_init(>buf_lock);
 
spi_controller_set_devdata(master, dws);
 
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 51bab30b9f85..1ab704d1ebd8 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -131,7 +131,6 @@ struct dw_spi {
size_t  len;
void*tx;
void*tx_end;
-   spinlock_t  buf_lock;
void*rx;
void*rx_end;
int dma_mapped;
-- 
2.27.0



[PATCH 11/30] spi: dw: Add DWC SSI capability

2020-09-20 Thread Serge Semin
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.

Signed-off-by: Serge Semin 
---
 drivers/spi/spi-dw-core.c | 69 ---
 drivers/spi/spi-dw-mmio.c | 20 ++--
 drivers/spi/spi-dw.h  |  9 +
 3 files changed, 23 insertions(+), 75 deletions(-)

diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 8f9737640ec1..c21641a485ce 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -228,60 +228,33 @@ 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);
-
-   return cr0;
-}
-EXPORT_SYMBOL_GPL(dw_spi_update_cr0);
-
-/* 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;
-
-   /* CTRLR0[ 4: 0] Data Frame Size */
cr0 = (transfer->bits_per_word - 1);
 
-   /* 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;
-
-   /* 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;
-
-   if (dws->caps & DW_SPI_CAP_KEEMBAY_MST)
-   cr0 |= DWC_SSI_CTRLR0_KEEMBAY_MST;
+   if (!(dws->caps & DW_SPI_CAP_DWC_SSI)) {
+   cr0 |= SSI_MOTO_SPI << SPI_FRF_OFFSET;
+   cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET;
+   cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET;
+   cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET;
+   cr0 |= chip->tmode << SPI_TMOD_OFFSET;
+   } else {
+   cr0 |= SSI_MOTO_SPI << DWC_SSI_CTRLR0_FRF_OFFSET;
+   cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << 
DWC_SSI_CTRLR0_SCPOL_OFFSET;
+   cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << 
DWC_SSI_CTRLR0_SCPH_OFFSET;
+   cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << 
DWC_SSI_CTRLR0_SRL_OFFSET;
+   cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET;
+
+   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);
 
 

[PATCH 16/30] spi: dw: Add DW SPI controller config structure

2020-09-20 Thread Serge Semin
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 
---
 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 f00fc4828480..9102685c1523 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -20,10 +20,8 @@
 #include 
 #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 */
 };
@@ -248,25 +246,28 @@ static u32 dw_spi_get_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;
u32 speed_hz;
u16 clk_div;
 
-   cr0 |= (transfer->bits_per_word - 1);
+   cr0 |= (cfg->dfs - 1);
 
if (!(dws->caps & DW_SPI_CAP_DWC_SSI))
-   cr0 |= chip->tmode << SPI_TMOD_OFFSET;
+   cr0 |= cfg->tmode << SPI_TMOD_OFFSET;
else
-   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) {
@@ -280,11 +281,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;
@@ -302,7 +309,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, );
 
transfer->effective_speed_hz = dws->current_freq;
 
@@ -388,8 +395,6 @@ static int dw_spi_setup(struct spi_device *spi)
 */
chip->cr0 = dw_spi_get_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



[PATCH 27/30] spi: dw: Introduce max mem-ops SPI bus frequency setting

2020-09-20 Thread Serge Semin
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 
---
 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 ca22f427d82d..7b901226fd38 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -608,7 +608,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;
@@ -695,6 +695,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



[PATCH 18/30] spi: dw: Refactor IRQ-based SPI transfer procedure

2020-09-20 Thread Serge Semin
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 
---
 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 84c1fdfd6e52..682463b2f68b 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;
@@ -317,10 +330,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) {
@@ -328,12 +337,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



[PATCH 19/30] spi: dw: Perform IRQ setup in a dedicated function

2020-09-20 Thread Serge Semin
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 
---
 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 682463b2f68b..08bc53b9de88 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);
 
@@ -294,6 +294,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)
 {
@@ -303,8 +324,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;
@@ -337,21 +356,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



[PATCH 05/30] spi: dw: Clear IRQ status on DW SPI controller reset

2020-09-20 Thread Serge Semin
It turns out the IRQ status isn't cleared after switching the controller
off and getting it back on, which may cause raising false error interrupts
if controller has been unsuccessfully used by, for instance, a bootloader
before the driver is loaded. Let's explicitly clear the interrupts status
in the dedicated controller reset method.

Signed-off-by: Serge Semin 
---
 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 1ab704d1ebd8..ff77f39047ce 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -229,14 +229,15 @@ static inline void spi_umask_intr(struct dw_spi *dws, u32 
mask)
 }
 
 /*
- * This does disable the SPI controller, interrupts, 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 re-enable 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);
spi_enable_chip(dws, 1);
 }
 
-- 
2.27.0



[PATCH 17/30] spi: dw: Refactor data IO procedure

2020-09-20 Thread Serge Semin
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 
---
 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 9102685c1523..84c1fdfd6e52 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;
@@ -299,12 +297,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);
@@ -331,7 +328,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 --

[PATCH 22/30] spi: dw: De-assert chip-select on reset

2020-09-20 Thread Serge Semin
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 
---
 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



[PATCH 13/30] spi: dw: Update SPI bus speed in a config function

2020-09-20 Thread Serge Semin
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 
---
 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 a9644351d75f..0b80a16d9872 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -251,8 +251,8 @@ static u32 dw_spi_get_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;
@@ -265,6 +265,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,
@@ -289,21 +300,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



[PATCH 26/30] spi: dw: Add memory operations support

2020-09-20 Thread Serge Semin
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 
Signed-off-by: Ramil Zaripov 
Signed-off-by: Serge Semin 
---
 drivers/spi/Kconfig   |   1 +
 drivers/spi/spi-dw-core.c | 300 ++
 drivers/spi/spi-dw.h  |  13 ++
 3 files changed, 314 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 77d61ded9256..ca22f427d82d 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -8,10 +8,13 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 
 #include "spi-dw.h"
@@ -401,6 +404,300 @@ 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([i], op->data.buf.out, op->dat

[PATCH 24/30] spi: dw: Move num-of retries parameter to the header file

2020-09-20 Thread Serge Semin
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 
---
 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_BUSY0
 #define RX_BURST_LEVEL 16
 #define TX_BUSY1
@@ -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



[PATCH 28/30] spi: dw: Add poll-based SPI transfers support

2020-09-20 Thread Serge Semin
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 
---
 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 7b901226fd38..cd01225a0f81 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -343,6 +343,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(, 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)
 {
@@ -387,6 +423,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);
 
@@ -795,7 +833,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



[PATCH 14/30] spi: dw: Simplify the SPI bus speed config procedure

2020-09-20 Thread Serge Semin
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 
---
 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 0b80a16d9872..92138a6ada12 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 */
 };
@@ -256,6 +253,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;
 
cr0 |= (transfer->bits_per_word - 1);
 
@@ -266,15 +265,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;
}
 }
 
@@ -302,7 +299,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



<    1   2   3   4   5   6   7   8   9   10   >