Skip to contentSkip to conversation

Add support for components requiring trailing clock after transfer

PATCH Superseded Christophe Leroy 2 participants 5 messages Feb 27, 2022 – Feb 28, 2022

Subsystems (2)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover Add support for components requiring trailing clock after transfer

Some components require a few clock cycles with chipselect off before or/and after the data transfer done with CS on.

Typically IDT 801034 QUAD PCM CODEC datasheet states "Note *: CCLK should have one cycle before CS goes low, and two cycles after CS goes high".

The cycles "before" are implicitely provided by all previous activity on the SPI bus. But the cycles "after" must be provided in order to achieve the SPI transfer.

In order to use that kind of component, implement a new option for SPI slaves in order to implement a trailing clock of a few bits with ChipSelect off at the end of the transfer.

This is based on a discussion we had a few years ago, see
https://lore.kernel.org/linux-spi/[email protected]/

IDT 801034 QUAD PCM CODEC datasheet can be found at
https://www.renesas.com/eu/en/document/dst/821034-data-sheet?language=en&r=24763

Christophe Leroy (2):
spi: Add new mode to generate additional clock cycles
spi: fsl-spi: Implement trailing clock mode

 .../bindings/spi/spi-peripheral-props.yaml        |  4 ++++
 drivers/spi/spi-fsl-lib.c                         |  2 +-
 drivers/spi/spi-fsl-spi.c                         | 15 +++++++++++++--
 drivers/spi/spi.c                                 |  5 ++++-
 include/uapi/linux/spi/spi.h                      |  3 ++-
 5 files changed, 24 insertions(+), 5 deletions(-)
Signature · 1 line
2.34.1
#1 lore raw

PATCH 1/2 spi: Add new mode to generate additional clock cycles

Some components require a few clock cycles with chipselect off before or/and after the data transfer done with CS on.

Typically IDT 801034 QUAD PCM CODEC datasheet states "Note *: CCLK should have one cycle before CS goes low, and two cycles after CS goes high".

The cycles "before" are implicitely provided by all previous activity on the SPI bus. But the cycles "after" must be provided in order to achieve the SPI transfer.

In order to use that kind of component, implement a new option for SPI slaves in order to implement a trailing clock of a few bits with ChipSelect off at the end of the transfer.

---
 .../devicetree/bindings/spi/spi-peripheral-props.yaml        | 4 ++++
 drivers/spi/spi.c                                            | 5 ++++-
 include/uapi/linux/spi/spi.h                                 | 3 ++-
 3 files changed, 10 insertions(+), 2 deletions(-)
Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml +4 -0
index 5dd209206e88..4e4fc357d667 100644
@@ -82,6 +82,10 @@ properties:
     description:
       Delay, in microseconds, after a write transfer.
 
+  spi-trailing-clock:
+    description:
+      Add a few clock cycles (minimum 2) with chipselect OFF after transfers.
+
 # The controller specific properties go here.
 allOf:
   - $ref: cdns,qspi-nor-peripheral-props.yaml#
drivers/spi/spi.c +4 -1
index 4599b121d744..1b943e112751 100644
@@ -2109,6 +2109,8 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
 		spi->mode |= SPI_LSB_FIRST;
 	if (of_property_read_bool(nc, "spi-cs-high"))
 		spi->mode |= SPI_CS_HIGH;
+	if (of_property_read_bool(nc, "spi-trailing-clock"))
+		spi->mode |= SPI_TRAILING;
 
 	/* Device DUAL/QUAD mode */
 	if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
@@ -3538,12 +3540,13 @@ int spi_setup(struct spi_device *spi)
 
 	trace_spi_setup(spi, status);
 
-	dev_dbg(&spi->dev, "setup mode %lu, %s%s%s%s%u bits/w, %u Hz max --> %d\n",
+	dev_dbg(&spi->dev, "setup mode %lu, %s%s%s%s%s%u bits/w, %u Hz max --> %d\n",
 			spi->mode & SPI_MODE_X_MASK,
 			(spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
 			(spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",
 			(spi->mode & SPI_3WIRE) ? "3wire, " : "",
 			(spi->mode & SPI_LOOP) ? "loopback, " : "",
+			(spi->mode & SPI_TRAILING) ? "trailing clock, " : "",
 			spi->bits_per_word, spi->max_speed_hz,
 			status);
 
include/uapi/linux/spi/spi.h +2 -1
index 236a85f08ded..0933a619a154 100644
@@ -27,6 +27,7 @@
 #define	SPI_TX_OCTAL		_BITUL(13)	/* transmit with 8 wires */
 #define	SPI_RX_OCTAL		_BITUL(14)	/* receive with 8 wires */
 #define	SPI_3WIRE_HIZ		_BITUL(15)	/* high impedance turnaround */
+#define	SPI_TRAILING		_BITUL(16)	/* trailing clock needed */
 
 /*
  * All the bits defined above should be covered by SPI_MODE_USER_MASK.
@@ -36,6 +37,6 @@
  * These bits must not overlap. A static assert check should make sure of that.
  * If adding extra bits, make sure to increase the bit index below as well.
  */
-#define SPI_MODE_USER_MASK	(_BITUL(16) - 1)
+#define SPI_MODE_USER_MASK	(_BITUL(17) - 1)
 
 #endif /* _UAPI_SPI_H */
Signature · 1 line
2.34.1
maintainer
#2 lore raw

Re: [PATCH v1 1/2] spi: Add new mode to generate additional clock cycles

Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml properties: · line 88

Quoted 4 lines · Christophe Leroy
Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml +4 -0
+  spi-trailing-clock:
+    description:
+      Add a few clock cycles (minimum 2) with chipselect OFF after transfers.
+

We should make this a specification of the actual requirement, not just a boolean with a vague "a few" requirement - with the above we'd have to add a new property if some device turns up which requires three clocks instead of two.

#3 lore raw

PATCH 2/2 spi: fsl-spi: Implement trailing clock mode

In reply to Christophe Leroy

In order to support IDT 801034 QUAD PCM CODEC, implement the trailing clock mode.

On fsl SPI, the minimum we can implement is a 4 bits shot.

---
 drivers/spi/spi-fsl-lib.c |  2 +-
 drivers/spi/spi-fsl-spi.c | 15 +++++++++++++--
 2 files changed, 14 insertions(+), 3 deletions(-)
drivers/spi/spi-fsl-lib.c +1 -1
index 76e1192eb025..a13f3b4db55f 100644
@@ -88,7 +88,7 @@ void mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
 
 	/* the spi->mode bits understood by this driver: */
 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
-			| SPI_LSB_FIRST | SPI_LOOP;
+			| SPI_LSB_FIRST | SPI_LOOP | SPI_TRAILING;
 
 	master->dev.of_node = dev->of_node;
 
drivers/spi/spi-fsl-spi.c +13 -2
index bdf94cc7be1a..6a52955d9051 100644
@@ -424,13 +424,24 @@ static int fsl_spi_do_one_msg(struct spi_master *master,
 		}
 	}
 
-	m->status = status;
-
 	if (status || !cs_change) {
 		ndelay(nsecs);
 		fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE);
 	}
 
+	if (!status && spi->mode & SPI_TRAILING) {
+		struct spi_transfer t = {
+			.len = 1,
+			.tx_buf = "",
+			.bits_per_word = 4
+		};
+
+		status = fsl_spi_setup_transfer(spi, &t);
+		if (!status)
+			status = fsl_spi_bufs(spi, &t, 0);
+	}
+	m->status = status;
+
 	fsl_spi_setup_transfer(spi, NULL);
 	spi_finalize_current_message(master);
 	return 0;
Signature · 1 line
2.34.1
maintainer
#4 lore raw

Re: [PATCH v1 2/2] spi: fsl-spi: Implement trailing clock mode

drivers/spi/spi-fsl-spi.c static int fsl_spi_do_one_msg(struct spi_master *master, · line 443

Quoted 12 lines · Christophe Leroy
drivers/spi/spi-fsl-spi.c +12 -0
+	if (!status && spi->mode & SPI_TRAILING) {
+		struct spi_transfer t = {
+			.len = 1,
+			.tx_buf = "",
+			.bits_per_word = 4
+		};
+
+		status = fsl_spi_setup_transfer(spi, &t);
+		if (!status)
+			status = fsl_spi_bufs(spi, &t, 0);
+	}
+	m->status = status;

This seems to be begging for a generic implementation in the core rather than being driver specific - drivers would for the most part need updating to advertise less than 8 bit per word transfers but the basic operation isn't really device specific and it pretty much fits with the existing interfaces.