Skip to content

Commit

Permalink
spi_nxp_lpspi: RX bigger than TX read all RX
Browse files Browse the repository at this point in the history
Change the driver behavior so that if the provided RX buffer set is
bigger than the TX buffer, we will read all the RX buffer and fill the
TX with NOPs. The SPI driver API does not say to do this, and my
original interpretation of the API was that the TX length controls the
entire transfer length, but this behavior might fit better with some de
facto expectations of in tree consumers.

Also add some robustness to the calculation of how many extra bytes to fill
when tx should be nops.

Signed-off-by: Declan Snyder <[email protected]>
  • Loading branch information
decsny authored and kartben committed Feb 20, 2025
1 parent b5ca5b7 commit 2a35835
Showing 1 changed file with 39 additions and 52 deletions.
91 changes: 39 additions & 52 deletions drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ static inline void lpspi_wait_tx_fifo_empty(const struct device *dev)
}
}

static inline uint8_t rx_fifo_cur_len(LPSPI_Type *base)
{
return (base->FSR & LPSPI_FSR_RXCOUNT_MASK) >> LPSPI_FSR_RXCOUNT_SHIFT;
}

static inline uint8_t tx_fifo_cur_len(LPSPI_Type *base)
{
return (base->FSR & LPSPI_FSR_TXCOUNT_MASK) >> LPSPI_FSR_TXCOUNT_SHIFT;
}


/* Reads a word from the RX fifo and handles writing it into the RX spi buf */
static inline void lpspi_rx_word_write_bytes(const struct device *dev, size_t offset)
{
Expand Down Expand Up @@ -66,21 +77,16 @@ static inline size_t lpspi_rx_buf_write_words(const struct device *dev, uint8_t
return words_read;
}

static inline uint8_t rx_fifo_cur_len(LPSPI_Type *base)
{
return (base->FSR & LPSPI_FSR_RXCOUNT_MASK) >> LPSPI_FSR_RXCOUNT_SHIFT;
}

static inline void lpspi_handle_rx_irq(const struct device *dev)
{
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
struct spi_mcux_data *data = dev->data;
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
struct spi_context *ctx = &data->ctx;
uint8_t rx_fsr = rx_fifo_cur_len(base);
uint8_t total_words_written = 0;
uint8_t total_words_read = 0;
uint8_t words_read;
uint8_t rx_fsr;

LPSPI_ClearStatusFlags(base, kLPSPI_RxDataReadyFlag);

Expand All @@ -95,7 +101,7 @@ static inline void lpspi_handle_rx_irq(const struct device *dev)

LOG_DBG("RX done %d words to spi buf", total_words_written);

if (!spi_context_rx_on(ctx)) {
if (spi_context_rx_len_left(ctx) == 0) {
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_RxInterruptEnable);
LPSPI_FlushFifo(base, false, true);
}
Expand Down Expand Up @@ -132,7 +138,7 @@ static inline void lpspi_fill_tx_fifo(const struct device *dev)
LOG_DBG("Filled TX FIFO to %d words (%d bytes)", lpspi_data->fill_len, offset);
}

static inline void lpspi_fill_tx_fifo_nop(const struct device *dev)
static void lpspi_fill_tx_fifo_nop(const struct device *dev)
{
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
struct spi_mcux_data *data = dev->data;
Expand Down Expand Up @@ -176,55 +182,21 @@ static inline void lpspi_handle_tx_irq(const struct device *dev)

LPSPI_ClearStatusFlags(base, kLPSPI_TxDataRequestFlag);

/* Having no buffer length left indicates transfer is done, if there
* was RX to do left, the TX buf would be null but
* ctx still tracks length of dummy data
*/
if (!spi_context_tx_on(ctx)) {
/* Disable chip select and end transfer clocks last word */
base->TCR = 0;
lpspi_wait_tx_fifo_empty(dev);
spi_context_cs_control(ctx, false);
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable);
return;
}

lpspi_next_tx_fill(data->dev);
}

static inline bool lpspi_is_rx_done(const struct device *dev)
{
struct spi_mcux_data *data = dev->data;
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
struct spi_context *ctx = &data->ctx;
size_t tx_total = lpspi_data->tx_total_len;
size_t rx_total = lpspi_data->rx_total_len;

if (tx_total >= rx_total) {
return (spi_context_total_rx_len(ctx) == 0);
} else {
return (tx_total <= (rx_total - spi_context_rx_len_left(ctx)));
}
}

static inline void lpspi_clear_remaining_rx(struct spi_context *ctx)
{
size_t remaining_len;

while ((remaining_len = spi_context_rx_len_left(ctx)) > 0) {
for (int i = 0; i < ctx->rx_len; i++) {
ctx->rx_buf[i] = 0;
}
spi_context_update_rx(ctx, 1, ctx->rx_len);
}
}

static void lpspi_isr(const struct device *dev)
{
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
const struct spi_mcux_config *config = dev->config;
uint32_t status_flags = LPSPI_GetStatusFlags(base);
struct spi_mcux_data *data = dev->data;
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
struct spi_context *ctx = &data->ctx;

if (status_flags & kLPSPI_RxDataReadyFlag) {
Expand All @@ -235,10 +207,29 @@ static void lpspi_isr(const struct device *dev)
lpspi_handle_tx_irq(dev);
}

if (!spi_context_tx_on(ctx) && lpspi_is_rx_done(dev)) {
if (spi_context_tx_on(ctx)) {
return;
}

if (spi_context_rx_len_left(ctx) == 1) {
base->TCR = 0;
} else if (spi_context_rx_on(ctx)) {
size_t rx_fifo_len = rx_fifo_cur_len(base);
size_t expected_rx_left = rx_fifo_len < ctx->rx_len ? ctx->rx_len - rx_fifo_len : 0;
size_t max_fill = MIN(expected_rx_left, config->rx_fifo_size);
size_t tx_current_fifo_len = tx_fifo_cur_len(base);

lpspi_data->fill_len = tx_current_fifo_len < ctx->rx_len ?
max_fill - tx_current_fifo_len : 0;

lpspi_fill_tx_fifo_nop(dev);
} else {
spi_context_complete(ctx, dev, 0);
NVIC_ClearPendingIRQ(config->irqn);
lpspi_clear_remaining_rx(ctx);
base->TCR = 0;
lpspi_wait_tx_fifo_empty(dev);
spi_context_cs_control(ctx, false);
spi_context_release(&data->ctx, 0);
}
}

Expand All @@ -257,7 +248,7 @@ static int transceive(const struct device *dev, const struct spi_config *spi_cfg
if (lpspi_data->word_size_bytes > 4) {
LOG_ERR("Maximum 4 byte word size");
ret = -EINVAL;
goto out;
return ret;
}

spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, lpspi_data->word_size_bytes);
Expand All @@ -267,7 +258,7 @@ static int transceive(const struct device *dev, const struct spi_config *spi_cfg

ret = spi_mcux_configure(dev, spi_cfg);
if (ret) {
goto out;
return ret;
}

LPSPI_FlushFifo(base, true, true);
Expand All @@ -291,11 +282,7 @@ static int transceive(const struct device *dev, const struct spi_config *spi_cfg
LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable |
(uint32_t)kLPSPI_RxInterruptEnable);

ret = spi_context_wait_for_completion(&data->ctx);
out:
spi_context_release(&data->ctx, ret);

return ret;
return spi_context_wait_for_completion(&data->ctx);
}

static int spi_mcux_transceive_sync(const struct device *dev, const struct spi_config *spi_cfg,
Expand Down

0 comments on commit 2a35835

Please sign in to comment.