diff --git a/sys/dev/intel/spi.c b/sys/dev/intel/spi.c --- a/sys/dev/intel/spi.c +++ b/sys/dev/intel/spi.c @@ -304,8 +304,8 @@ intelspi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct intelspi_softc *sc; - int err; - uint32_t sscr0, sscr1, mode, clock; + int err, poll_limit; + uint32_t sscr0, sscr1, mode, clock, cs_delay; bool restart = false; sc = device_get_softc(dev); @@ -377,19 +377,34 @@ /* Enable CS */ intelspi_set_cs(sc, CS_LOW); - /* Transfer as much as possible to FIFOs */ - if (!intelspi_transact(sc)) { - /* If FIFO is not large enough - enable interrupts */ - sscr1 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1); - sscr1 |= (SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE); - INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, sscr1); - /* and wait for transaction to complete */ - err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", hz * 2); + /* Wait the CS delay */ + spibus_get_cs_delay(child, &cs_delay); + DELAY(cs_delay); + + /* Transfer as much as possible to FIFOs */ + if ((cmd->flags & SPI_FLAG_ALLOW_SLEEP) == 0) { + /* We cannot wait with mtx_sleep if we're called from e.g. an ithread */ + poll_limit = 100; + while (!intelspi_transact(sc) && poll_limit-- > 0) + DELAY(1000); + if (poll_limit == 0) + device_printf(dev, "polling was stuck, transaction not finished\n"); + } else { + if (!intelspi_transact(sc)) { + /* If FIFO is not large enough - enable interrupts */ + sscr1 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1); + sscr1 |= (SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE); + INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, sscr1); + + /* and wait for transaction to complete */ + err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", hz * 2); + } } - /* de-asser CS */ - intelspi_set_cs(sc, CS_HIGH); + /* De-assert CS */ + if ((cmd->flags & SPI_FLAG_KEEP_CS) == 0) + intelspi_set_cs(sc, CS_HIGH); /* Clear transaction details */ sc->sc_cmd = NULL; diff --git a/sys/dev/spibus/spi.h b/sys/dev/spibus/spi.h --- a/sys/dev/spibus/spi.h +++ b/sys/dev/spibus/spi.h @@ -34,9 +34,13 @@ uint32_t tx_data_sz; void *rx_data; uint32_t rx_data_sz; + uint32_t flags; }; #define SPI_COMMAND_INITIALIZER { 0 } +#define SPI_FLAG_KEEP_CS 0x1 /* Keep chip select asserted */ +#define SPI_FLAG_ALLOW_SLEEP 0x2 /* Allow the driver to sleep */ + #define SPI_CHIP_SELECT_HIGH 0x1 /* Chip select high (else low) */ #ifdef FDT diff --git a/sys/dev/spibus/spibus.c b/sys/dev/spibus/spibus.c --- a/sys/dev/spibus/spibus.c +++ b/sys/dev/spibus/spibus.c @@ -146,6 +146,9 @@ case SPIBUS_IVAR_CLOCK: *(uint32_t *)result = devi->clock; break; + case SPIBUS_IVAR_CS_DELAY: + *(uint32_t *)result = devi->cs_delay; + break; } return (0); } @@ -174,6 +177,9 @@ return (EINVAL); devi->mode = (uint32_t)value; break; + case SPIBUS_IVAR_CS_DELAY: + devi->cs_delay = (uint32_t)value; + break; default: return (EINVAL); } diff --git a/sys/dev/spibus/spibusvar.h b/sys/dev/spibus/spibusvar.h --- a/sys/dev/spibus/spibusvar.h +++ b/sys/dev/spibus/spibusvar.h @@ -43,6 +43,7 @@ uint32_t cs; uint32_t mode; uint32_t clock; + uint32_t cs_delay; struct resource_list rl; }; @@ -52,6 +53,7 @@ SPIBUS_IVAR_CS, /* chip select that we're on */ SPIBUS_IVAR_MODE, /* SPI mode (0-3) */ SPIBUS_IVAR_CLOCK, /* maximum clock freq for device */ + SPIBUS_IVAR_CS_DELAY, /* delay in microseconds after toggling chip select */ }; #define SPIBUS_ACCESSOR(A, B, T) \ @@ -71,6 +73,7 @@ SPIBUS_ACCESSOR(cs, CS, uint32_t) SPIBUS_ACCESSOR(mode, MODE, uint32_t) SPIBUS_ACCESSOR(clock, CLOCK, uint32_t) +SPIBUS_ACCESSOR(cs_delay, CS_DELAY, uint32_t) extern driver_t spibus_driver; extern devclass_t spibus_devclass;