git: 8ef8939fd458 - main - bcm2835/spi: Support SPI_FLAG_KEEP_CS

From: Justin Hibbits <jhibbits_at_FreeBSD.org>
Date: Tue, 14 Nov 2023 21:48:35 UTC
The branch main has been updated by jhibbits:

URL: https://cgit.FreeBSD.org/src/commit/?id=8ef8939fd4581572e17860d7089cc6e06fd0221f

commit 8ef8939fd4581572e17860d7089cc6e06fd0221f
Author:     Justin Hibbits <jhibbits@FreeBSD.org>
AuthorDate: 2023-11-02 20:09:14 +0000
Commit:     Justin Hibbits <jhibbits@FreeBSD.org>
CommitDate: 2023-11-14 21:47:35 +0000

    bcm2835/spi: Support SPI_FLAG_KEEP_CS
    
    Summary:
    3c08673438 brought in SPI_FLAG_KEEP_CS to keep the SPI chip select held
    post-transfer completion.  Add this support to bcm2835 SPI for SPI
    devices that need it.  As part of this, the owner thread needed carried
    through so that no other thread can take over the SPI bus until the
    owner releases the chip select.
    
    Reviewed by:    manu
    Sponsored by:   Juniper Networks, Inc.
    Differential Revision:  https://reviews.freebsd.org/D42599
---
 sys/arm/broadcom/bcm2835/bcm2835_spi.c    | 33 +++++++++++++++++++++----------
 sys/arm/broadcom/bcm2835/bcm2835_spivar.h |  2 ++
 2 files changed, 25 insertions(+), 10 deletions(-)

diff --git a/sys/arm/broadcom/bcm2835/bcm2835_spi.c b/sys/arm/broadcom/bcm2835/bcm2835_spi.c
index 7d11e6aa50f7..b7f53be2da8d 100644
--- a/sys/arm/broadcom/bcm2835/bcm2835_spi.c
+++ b/sys/arm/broadcom/bcm2835/bcm2835_spi.c
@@ -388,8 +388,10 @@ bcm_spi_intr(void *arg)
 	/* Check for end of transfer. */
 	if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) {
 		/* Disable interrupts and the SPI engine. */
-		bcm_spi_modifyreg(sc, SPI_CS,
-		    SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0);
+		if ((sc->sc_flags & BCM_SPI_KEEP_CS) == 0) {
+			bcm_spi_modifyreg(sc, SPI_CS,
+			    SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0);
+		}
 		wakeup(sc->sc_dev);
 	}
 
@@ -438,16 +440,23 @@ bcm_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 
 	/* If the controller is in use wait until it is available. */
 	BCM_SPI_LOCK(sc);
-	while (sc->sc_flags & BCM_SPI_BUSY)
-		mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", 0);
+	if (sc->sc_thread != curthread)
+		while (sc->sc_flags & BCM_SPI_BUSY)
+			mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", 0);
 
 	/* Now we have control over SPI controller. */
 	sc->sc_flags = BCM_SPI_BUSY;
 
+	if ((cmd->flags & SPI_FLAG_KEEP_CS) != 0)
+		sc->sc_flags |= BCM_SPI_KEEP_CS;
+
 	/* Clear the FIFO. */
-	bcm_spi_modifyreg(sc, SPI_CS,
-	    SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO,
-	    SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO);
+	if (sc->sc_thread != curthread)
+		bcm_spi_modifyreg(sc, SPI_CS,
+		    SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO,
+		    SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO);
+
+	sc->sc_thread = curthread;
 
 	/* Save a pointer to the SPI command. */
 	sc->sc_cmd = cmd;
@@ -517,11 +526,15 @@ bcm_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 	err = mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", hz * 2);
 
 	/* Make sure the SPI engine and interrupts are disabled. */
-	bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0);
+	if (!(cmd->flags & SPI_FLAG_KEEP_CS)) {
+		bcm_spi_modifyreg(sc,
+		    SPI_CS, SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0);
+		sc->sc_thread = 0;
+	}
 
-	/* Release the controller and wakeup the next thread waiting for it. */
-	sc->sc_flags = 0;
 	wakeup_one(dev);
+	sc->sc_flags &= ~BCM_SPI_BUSY;
+	/* Release the controller and wakeup the next thread waiting for it. */
 	BCM_SPI_UNLOCK(sc);
 
 	/*
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_spivar.h b/sys/arm/broadcom/bcm2835/bcm2835_spivar.h
index 1c9a81edc777..85b13626a026 100644
--- a/sys/arm/broadcom/bcm2835/bcm2835_spivar.h
+++ b/sys/arm/broadcom/bcm2835/bcm2835_spivar.h
@@ -36,6 +36,7 @@ struct bcm_spi_softc {
 	struct resource *	sc_mem_res;
 	struct resource *	sc_irq_res;
 	struct spi_command	*sc_cmd;
+	struct thread		*sc_thread;
 	bus_space_tag_t		sc_bst;
 	bus_space_handle_t	sc_bsh;
 	uint32_t		sc_len;
@@ -46,6 +47,7 @@ struct bcm_spi_softc {
 };
 
 #define	BCM_SPI_BUSY		0x1
+#define	BCM_SPI_KEEP_CS		0x2
 
 #define BCM_SPI_WRITE(_sc, _off, _val)		\
     bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val)