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
This commit is contained in:
Justin Hibbits 2023-11-02 16:09:14 -04:00
parent 029848334f
commit 8ef8939fd4
2 changed files with 25 additions and 10 deletions

View file

@ -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);
/*

View file

@ -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)