Skip to content

spi: sam0: fix txrx, NULL buffers, and add a stub async method. #5731

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 81 additions & 55 deletions drivers/spi/spi_sam0.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 */
Expand All @@ -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) {
Expand All @@ -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++;
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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,
};

Expand Down
13 changes: 13 additions & 0 deletions tests/drivers/spi/spi_loopback/src/spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down