diff --git a/drivers/spi/spi_sam0.c b/drivers/spi/spi_sam0.c index 543995ae1232..38f74d08a87b 100644 --- a/drivers/spi/spi_sam0.c +++ b/drivers/spi/spi_sam0.c @@ -128,7 +128,7 @@ static void spi_sam0_shift_master(SercomSpi *regs, struct spi_sam0_data *data) u8_t tx; u8_t rx; - if (spi_context_tx_on(&data->ctx)) { + if (spi_context_tx_buf_on(&data->ctx)) { tx = *(u8_t *)(data->ctx.tx_buf); } else { tx = 0; @@ -145,10 +145,10 @@ static void spi_sam0_shift_master(SercomSpi *regs, struct spi_sam0_data *data) rx = regs->DATA.reg; - if (spi_context_rx_on(&data->ctx)) { + if (spi_context_rx_buf_on(&data->ctx)) { *data->ctx.rx_buf = rx; - spi_context_update_rx(&data->ctx, 1, 1); } + spi_context_update_rx(&data->ctx, 1, 1); } /* Fast path that transmits a buf */ @@ -173,103 +173,109 @@ static void spi_sam0_fast_tx(SercomSpi *regs, const struct spi_buf *tx_buf) /* Fast path that reads into a buf */ static void spi_sam0_fast_rx(SercomSpi *regs, struct spi_buf *rx_buf) { - u8_t *p = rx_buf->buf; - size_t len = rx_buf->len; - - while (regs->INTFLAG.bit.RXC) { - (void)regs->DATA.reg; - } + u8_t *rx = rx_buf->buf; + int len = rx_buf->len; if (len <= 0) { return; } - /* - * The code below interleaves the transmit of the next byte - * with the receive of the next. The code is equivalent to: - * - * Transmit byte 0 - * Loop: - * - Transmit byte n+1 - * - Receive byte n - */ + /* See the comment in spi_sam0_fast_txrx re: interleaving. */ - /* Load the first outgoing byte */ + /* Ensure transmit is idle */ while (!regs->INTFLAG.bit.DRE) { } + /* Flush the receive buffer */ + while (regs->INTFLAG.bit.RXC) { + (void)regs->DATA.reg; + } + + /* Write the first byte */ regs->DATA.reg = 0; + len--; while (len) { - if (len != 0) { - while (!regs->INTFLAG.bit.DRE) { - } - - regs->DATA.reg = 0; - } - - /* - * Decrement len while waiting for the transfer to - * complete. - */ + /* Load byte N+1 into the transmit register */ + regs->DATA.reg = 0; len--; + /* Read byte N+0 from the receive register */ while (!regs->INTFLAG.bit.RXC) { } - *p++ = regs->DATA.reg; + *rx++ = regs->DATA.reg; } - /* Note that all transmits are complete and the RX buf is empty */ + /* Read the final incoming byte */ + while (!regs->INTFLAG.bit.RXC) { + } + + *rx = regs->DATA.reg; } /* Fast path that writes and reads bufs of the same length */ static void spi_sam0_fast_txrx(SercomSpi *regs, const struct spi_buf *tx_buf, struct spi_buf *rx_buf) { - const u8_t *psrc = tx_buf->buf; - u8_t *p = rx_buf->buf; + const u8_t *tx = tx_buf->buf; + const u8_t *txend = tx_buf->buf + tx_buf->len; + u8_t *rx = rx_buf->buf; size_t len = rx_buf->len; - while (regs->INTFLAG.bit.RXC) { - (void)regs->DATA.reg; - } - if (len <= 0) { return; } - /* See the comment in spi_sam0_fast_rx re: interleaving. */ + /* + * The code below interleaves the transmit writes with the + * receive reads to keep the bus fully utilised. The code is + * equivalent to: + * + * Transmit byte 0 + * Loop: + * - Transmit byte n+1 + * - Receive byte n + * Receive the final byte + */ - /* Load the first outgoing byte */ + /* Ensure transmit is idle */ while (!regs->INTFLAG.bit.DRE) { } - regs->DATA.reg = *psrc++; - - while (len) { - if (len != 0) { - while (!regs->INTFLAG.bit.DRE) { - } + /* Flush the receive buffer */ + while (regs->INTFLAG.bit.RXC) { + (void)regs->DATA.reg; + } - regs->DATA.reg = *psrc++; - } + /* Write the first byte */ + regs->DATA.reg = *tx++; - len--; + while (tx != txend) { + /* Load byte N+1 into the transmit register. TX is + * single buffered and we have at most one byte in + * flight so skip the DRE check. + */ + regs->DATA.reg = *tx++; + /* Read byte N+0 from the receive register */ while (!regs->INTFLAG.bit.RXC) { } - *p++ = regs->DATA.reg; + *rx++ = regs->DATA.reg; + } + + /* Read the final incoming byte */ + while (!regs->INTFLAG.bit.RXC) { } - /* Note that all transmits are complete and the RX buf is empty */ + *rx = regs->DATA.reg; } /* Finish any ongoing writes and drop any remaining read data */ static void spi_sam0_finish(SercomSpi *regs) { - while (!regs->INTFLAG.bit.TXC) { + while (!regs->INTFLAG.bit.DRE) { } while (regs->INTFLAG.bit.RXC) { @@ -287,7 +293,13 @@ static void spi_sam0_fast_transceive(struct spi_config *config, SercomSpi *regs = cfg->regs; while (tx_count != 0 && rx_count != 0) { - spi_sam0_fast_txrx(regs, tx_bufs, rx_bufs); + if (tx_bufs->buf == NULL) { + spi_sam0_fast_rx(regs, rx_bufs); + } else if (rx_bufs->buf == NULL) { + spi_sam0_fast_tx(regs, tx_bufs); + } else { + spi_sam0_fast_txrx(regs, tx_bufs, rx_bufs); + } tx_bufs++; tx_count--; rx_bufs++; @@ -350,10 +362,10 @@ static int spi_sam0_transceive(struct spi_config *config, spi_context_cs_configure(&data->ctx); spi_context_cs_control(&data->ctx, true); - /* This driver special case for the common send only, receive + /* This driver special cases the common send only, receive * only, and transmit then receive operations. This special * casing is 4x faster than the spi_context() routines - * and also allows the transmit and receive to be interleaved. + * and allows the transmit and receive to be interleaved. */ if (spi_sam0_is_regular(tx_bufs, tx_count, rx_bufs, rx_count)) { spi_sam0_fast_transceive(config, tx_bufs, tx_count, rx_bufs, @@ -374,6 +386,17 @@ static int spi_sam0_transceive(struct spi_config *config, return err; } +#ifdef CONFIG_POLL +static int spi_sam0_transceive_async(struct spi_config *config, + const struct spi_buf *tx_bufs, + size_t tx_count, struct spi_buf *rx_bufs, + size_t rx_count, + struct k_poll_signal *async) +{ + return -ENOTSUP; +} +#endif + static int spi_sam0_release(struct spi_config *config) { struct spi_sam0_data *data = config->dev->driver_data; @@ -416,6 +439,9 @@ static int spi_sam0_init(struct device *dev) static const struct spi_driver_api spi_sam0_driver_api = { .transceive = spi_sam0_transceive, +#ifdef CONFIG_POLL + .transceive_async = spi_sam0_transceive_async, +#endif .release = spi_sam0_release, }; diff --git a/tests/drivers/spi/spi_loopback/src/spi.c b/tests/drivers/spi/spi_loopback/src/spi.c index f8c26aca4fe4..a6f015ec6d64 100644 --- a/tests/drivers/spi/spi_loopback/src/spi.c +++ b/tests/drivers/spi/spi_loopback/src/spi.c @@ -75,6 +75,19 @@ struct spi_cs_control spi_cs = { .delay = 0, }; +#elif defined(CONFIG_BOARD_ARDUINO_ZERO) + +#define SPI_DRV_NAME CONFIG_SPI_4_NAME +#define SPI_SLAVE 0 +#define MIN_FREQ 500000 + +#define CS_CTRL_GPIO_DRV_NAME CONFIG_GPIO_SAM0_PORTA_LABEL + +struct spi_cs_control spi_cs = { + .gpio_pin = 3, + .delay = 0, +}; + #else #undef SPI_CS #define SPI_CS NULL