spi_nxp_lpspi: Support SPI_HOLD_ON_CS FLAG

Support SPI_HOLD_ON_CS flag in the CPU-based driver. To do this we will
set CONTC bit to continue previous command. Technically it may not be
necessary right now, and could just not clear CONT bit...
but in the future in the lpspi driver we
will decouple the config/init of a transfer from the SDK
and therefore have more control over TCR,
and when we write the TCR, we need to take CONTC bit into account
otherwise a new command will be made. So this approach is how
it should be handled in the driver going forward in my opinion, even
if it might be possible without this bit right now, I want to introduce
it's usage now.

This commit also does a minor refactor in the ISR and adds some comments
to make the strange CS behavior and strange handling code more clear to
future readers.

Also, make the early predicted SPI xfer end code only happen for spi
versions where it is necessary, since I think that code is really the
best we can do but might have a race condition, where possible the last
word is not finished sending when we end the xfer. So limit the
potential affect to v1 lpspi where the workaround is actually required
due to stalling behavior.

Lastly, set the LPSPI into master mode at active low in init, due to
it being the most common case, we want the SPI CS lines to be
initialized at init of driver. I don't think it's worth it to make it
configurable at this time, but in the future it could be if needed.

Signed-off-by: Declan Snyder <declan.snyder@nxp.com>
This commit is contained in:
Declan Snyder 2025-04-22 17:22:43 -05:00 committed by Benjamin Cabé
commit e71aa649b2

View file

@ -219,6 +219,23 @@ static inline void lpspi_handle_tx_irq(const struct device *dev)
lpspi_next_tx_fill(data->dev);
}
static inline void lpspi_end_xfer(const struct device *dev)
{
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
const struct lpspi_config *config = dev->config;
struct lpspi_data *data = dev->data;
struct spi_context *ctx = &data->ctx;
spi_context_complete(ctx, dev, 0);
NVIC_ClearPendingIRQ(config->irqn);
if (!(ctx->config->operation & SPI_HOLD_ON_CS)) {
base->TCR &= ~(LPSPI_TCR_CONT_MASK | LPSPI_TCR_CONTC_MASK);
}
lpspi_wait_tx_fifo_empty(dev);
spi_context_cs_control(ctx, false);
spi_context_release(&data->ctx, 0);
}
static void lpspi_isr(const struct device *dev)
{
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
@ -253,15 +270,13 @@ static void lpspi_isr(const struct device *dev)
lpspi_data->fill_len = fill_len;
}
if (spi_context_rx_len_left(ctx) == 1) {
base->TCR &= ~LPSPI_TCR_CONT_MASK;
if (spi_context_rx_len_left(ctx) == 1 && (LPSPI_VERID_MAJOR(base->VERID) < 2)) {
/* Due to stalling behavior on older LPSPI,
* need to end xfer in order to get last bit clocked out on bus.
*/
base->TCR |= LPSPI_TCR_CONT_MASK;
} else if (spi_context_rx_len_left(ctx) == 0) {
spi_context_complete(ctx, dev, 0);
NVIC_ClearPendingIRQ(config->irqn);
base->TCR &= ~LPSPI_TCR_CONT_MASK;
lpspi_wait_tx_fifo_empty(dev);
spi_context_cs_control(ctx, false);
spi_context_release(&data->ctx, 0);
lpspi_end_xfer(dev);
}
}
@ -302,8 +317,16 @@ static int transceive(const struct device *dev, const struct spi_config *spi_cfg
base->CR |= LPSPI_CR_MEN_MASK;
/* keep the chip select asserted until the end of the zephyr xfer */
base->TCR |= LPSPI_TCR_CONT_MASK;
/* keep the chip select asserted until the end of the zephyr xfer by using
* continunous transfer mode. If SPI_HOLD_ON_CS is requested, we need
* to also set CONTC in order to continue the previous command to keep CS
* asserted.
*/
if (spi_cfg->operation & SPI_HOLD_ON_CS || base->TCR & LPSPI_TCR_CONTC_MASK) {
base->TCR |= LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK;
} else {
base->TCR |= LPSPI_TCR_CONT_MASK;
}
/* tcr is written to tx fifo */
lpspi_wait_tx_fifo_empty(dev);
@ -352,6 +375,7 @@ static DEVICE_API(spi, lpspi_driver_api) = {
static int lpspi_init(const struct device *dev)
{
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
struct lpspi_data *data = dev->data;
int err = 0;
@ -360,6 +384,14 @@ static int lpspi_init(const struct device *dev)
return err;
}
/* Starting config should be master with active low CS, to make sure
* the CS lines are configured properly at init for the most common use
* cases. This can be changed later on transceive call if user specifies
* different spi configuration.
*/
base->CFGR1 |= LPSPI_CFGR1_MASTER_MASK;
base->CFGR1 &= ~LPSPI_CFGR1_PCSPOL_MASK;
spi_context_unlock_unconditionally(&data->ctx);
return 0;