Skip to content

NXP LPSPI: Refactoring to remove dependency of HAL in XFER drivers #86939

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
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
22 changes: 15 additions & 7 deletions drivers/spi/spi_nxp_lpspi/Kconfig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2018, 2024 NXP
# Copyright 2018, 2024-2025 NXP
# SPDX-License-Identifier: Apache-2.0

config SPI_MCUX_LPSPI
Expand All @@ -8,24 +8,32 @@ config SPI_MCUX_LPSPI
depends on CLOCK_CONTROL
select PINCTRL
help
Enable support for NXP LPSPI.
Enable driver support for NXP LPSPI.

if SPI_MCUX_LPSPI

config SPI_MCUX_LPSPI_DMA
bool "MCUX LPSPI SPI DMA Support"
bool "NXP LPSPI DMA-based Driver"
default y
select DMA
depends on $(dt_compat_any_has_prop,$(DT_COMPAT_NXP_LPSPI),dmas)
help
Enable the SPI DMA mode for SPI instances
that enable dma channels in their device tree node.
Enable DMA-based transfers for LPSPI peripherals
that have a dmas property specified in their DT node.
The DMA based driver prioritizes bandwidth over latency, due to
DMA being more efficient for larger data transfers, to avoid CPU
having to be utilized to do the work. However, setting up a DMA
transfer is more complicated setup than just starting the transfer
immediately with CPU, so there could be more latency between
the point of requesting a transfer and when it actually starts.

config SPI_MCUX_LPSPI_CPU
bool "NXP MCUX LPSPI driver"
bool "NXP LPSPI CPU-based driver"
default y
depends on $(dt_compat_any_not_has_prop,$(DT_COMPAT_NXP_LPSPI),dmas) || !SPI_MCUX_LPSPI_DMA
help
Use the CPU-based LPSPI driver.
Enable "normal" CPU based SPI driver for LPSPI.
This has lower latency than DMA-based driver but over the
longer transfers will likely have less bandwidth and use more CPU time.

endif # SPI_MCUX_LPSPI
95 changes: 47 additions & 48 deletions drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#define DT_DRV_COMPAT nxp_lpspi

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(spi_mcux_lpspi, CONFIG_SPI_LOG_LEVEL);
LOG_MODULE_REGISTER(spi_lpspi, CONFIG_SPI_LOG_LEVEL);

#include "spi_nxp_lpspi_priv.h"

Expand All @@ -30,12 +30,12 @@ static inline uint8_t tx_fifo_cur_len(LPSPI_Type *base)
static inline void lpspi_rx_word_write_bytes(const struct device *dev, size_t offset)
{
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
struct spi_mcux_data *data = dev->data;
struct lpspi_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 num_bytes = MIN(lpspi_data->word_size_bytes, ctx->rx_len);
uint8_t *buf = ctx->rx_buf + offset;
uint32_t word = LPSPI_ReadData(base);
uint32_t word = base->RDR;

if (!spi_context_rx_buf_on(ctx) && spi_context_rx_on(ctx)) {
/* receive no actual data if rx buf is NULL */
Expand All @@ -50,7 +50,7 @@ static inline void lpspi_rx_word_write_bytes(const struct device *dev, size_t of
/* Reads a maximum number of words from RX fifo and writes them to the remainder of the RX buf */
static inline size_t lpspi_rx_buf_write_words(const struct device *dev, uint8_t max_read)
{
struct spi_mcux_data *data = dev->data;
struct lpspi_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 buf_len = DIV_ROUND_UP(ctx->rx_len, lpspi_data->word_size_bytes);
Expand All @@ -69,15 +69,15 @@ static inline size_t lpspi_rx_buf_write_words(const struct device *dev, uint8_t
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_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;

LPSPI_ClearStatusFlags(base, kLPSPI_RxDataReadyFlag);
base->SR = LPSPI_SR_RDF_MASK;

LOG_DBG("RX FIFO: %d, RX BUF: %p", rx_fsr, ctx->rx_buf);

Expand All @@ -91,14 +91,14 @@ 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_len_left(ctx) == 0) {
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_RxInterruptEnable);
LPSPI_FlushFifo(base, false, true);
base->IER &= ~LPSPI_IER_RDIE_MASK;
base->CR |= LPSPI_CR_RRF_MASK; /* flush rx fifo */
}
}

static inline uint32_t lpspi_next_tx_word(const struct device *dev, int offset)
{
struct spi_mcux_data *data = dev->data;
struct lpspi_data *data = dev->data;
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
struct spi_context *ctx = &data->ctx;
const uint8_t *byte = ctx->tx_buf + offset;
Expand All @@ -115,13 +115,13 @@ static inline uint32_t lpspi_next_tx_word(const struct device *dev, int offset)
static inline void lpspi_fill_tx_fifo(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_data *data = dev->data;
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
size_t bytes_in_xfer = lpspi_data->fill_len * lpspi_data->word_size_bytes;
size_t offset;

for (offset = 0; offset < bytes_in_xfer; offset += lpspi_data->word_size_bytes) {
LPSPI_WriteData(base, lpspi_next_tx_word(dev, offset));
base->TDR = lpspi_next_tx_word(dev, offset);
}

LOG_DBG("Filled TX FIFO to %d words (%d bytes)", lpspi_data->fill_len, offset);
Expand All @@ -130,20 +130,20 @@ static inline void lpspi_fill_tx_fifo(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;
struct lpspi_data *data = dev->data;
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;

for (int i = 0; i < lpspi_data->fill_len; i++) {
LPSPI_WriteData(base, 0);
base->TDR = 0;
}

LOG_DBG("Filled TX fifo with %d NOPs", lpspi_data->fill_len);
}

static void lpspi_next_tx_fill(const struct device *dev)
{
const struct spi_mcux_config *config = dev->config;
struct spi_mcux_data *data = dev->data;
const struct lpspi_config *config = dev->config;
struct lpspi_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 max_chunk;
Expand All @@ -163,16 +163,16 @@ static void lpspi_next_tx_fill(const struct device *dev)
static inline void lpspi_handle_tx_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_data *data = dev->data;
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
struct spi_context *ctx = &data->ctx;

spi_context_update_tx(ctx, lpspi_data->word_size_bytes, lpspi_data->fill_len);

LPSPI_ClearStatusFlags(base, kLPSPI_TxDataRequestFlag);
base->SR = LPSPI_SR_TDF_MASK;

if (!spi_context_tx_on(ctx)) {
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable);
base->IER &= ~LPSPI_IER_TDIE_MASK;
return;
}

Expand All @@ -182,17 +182,17 @@ static inline void lpspi_handle_tx_irq(const struct device *dev)
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;
const struct lpspi_config *config = dev->config;
struct lpspi_data *data = dev->data;
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
struct spi_context *ctx = &data->ctx;
uint32_t status_flags = base->SR;

if (status_flags & kLPSPI_RxDataReadyFlag) {
if (status_flags & LPSPI_SR_RDF_MASK) {
lpspi_handle_rx_irq(dev);
}

if (status_flags & kLPSPI_TxDataRequestFlag) {
if (status_flags & LPSPI_SR_TDF_MASK) {
lpspi_handle_tx_irq(dev);
}

Expand Down Expand Up @@ -229,7 +229,7 @@ static int transceive(const struct device *dev, const struct spi_config *spi_cfg
bool asynchronous, spi_callback_t cb, void *userdata)
{
LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
struct spi_mcux_data *data = dev->data;
struct lpspi_data *data = dev->data;
struct lpspi_driver_data *lpspi_data = (struct lpspi_driver_data *)data->driver_data;
struct spi_context *ctx = &data->ctx;
int ret = 0;
Expand All @@ -250,15 +250,15 @@ static int transceive(const struct device *dev, const struct spi_config *spi_cfg
goto error;
}

LPSPI_FlushFifo(base, true, true);
LPSPI_ClearStatusFlags(base, (uint32_t)kLPSPI_AllStatusFlag);
LPSPI_DisableInterrupts(base, (uint32_t)kLPSPI_AllInterruptEnable);
base->CR |= LPSPI_CR_RTF_MASK | LPSPI_CR_RRF_MASK; /* flush fifos */
base->IER = 0; /* disable all interrupts */
base->FCR = 0; /* set watermarks to 0 */
base->SR |= LPSPI_INTERRUPT_BITS;

LOG_DBG("Starting LPSPI transfer");
spi_context_cs_control(ctx, true);

LPSPI_SetFifoWatermarks(base, 0, 0);
LPSPI_Enable(base, true);
base->CR |= LPSPI_CR_MEN_MASK;

/* keep the chip select asserted until the end of the zephyr xfer */
base->TCR |= LPSPI_TCR_CONT_MASK;
Expand All @@ -268,8 +268,7 @@ static int transceive(const struct device *dev, const struct spi_config *spi_cfg
/* start the transfer sequence which are handled by irqs */
lpspi_next_tx_fill(dev);

LPSPI_EnableInterrupts(base, (uint32_t)kLPSPI_TxInterruptEnable |
(uint32_t)kLPSPI_RxInterruptEnable);
base->IER |= LPSPI_IER_TDIE_MASK | LPSPI_IER_RDIE_MASK;

ret = spi_context_wait_for_completion(ctx);
if (ret >= 0) {
Expand All @@ -281,15 +280,15 @@ static int transceive(const struct device *dev, const struct spi_config *spi_cfg
return ret;
}

static int spi_mcux_transceive_sync(const struct device *dev, const struct spi_config *spi_cfg,
static int lpspi_transceive_sync(const struct device *dev, const struct spi_config *spi_cfg,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs)
{
return transceive(dev, spi_cfg, tx_bufs, rx_bufs, false, NULL, NULL);
}

#ifdef CONFIG_SPI_ASYNC
static int spi_mcux_transceive_async(const struct device *dev, const struct spi_config *spi_cfg,
static int lpspi_transceive_async(const struct device *dev, const struct spi_config *spi_cfg,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs, spi_callback_t cb,
void *userdata)
Expand All @@ -298,20 +297,20 @@ static int spi_mcux_transceive_async(const struct device *dev, const struct spi_
}
#endif /* CONFIG_SPI_ASYNC */

static DEVICE_API(spi, spi_mcux_driver_api) = {
.transceive = spi_mcux_transceive_sync,
static DEVICE_API(spi, lpspi_driver_api) = {
.transceive = lpspi_transceive_sync,
#ifdef CONFIG_SPI_ASYNC
.transceive_async = spi_mcux_transceive_async,
.transceive_async = lpspi_transceive_async,
#endif
#ifdef CONFIG_SPI_RTIO
.iodev_submit = spi_rtio_iodev_default_submit,
#endif
.release = spi_mcux_release,
.release = spi_lpspi_release,
};

static int spi_mcux_init(const struct device *dev)
static int lpspi_init(const struct device *dev)
{
struct spi_mcux_data *data = dev->data;
struct lpspi_data *data = dev->data;
int err = 0;

err = spi_nxp_init_common(dev);
Expand All @@ -326,23 +325,23 @@ static int spi_mcux_init(const struct device *dev)

#define LPSPI_INIT(n) \
SPI_NXP_LPSPI_COMMON_INIT(n) \
SPI_MCUX_LPSPI_CONFIG_INIT(n) \
SPI_LPSPI_CONFIG_INIT(n) \
\
static struct lpspi_driver_data lpspi_##n##_driver_data; \
\
static struct spi_mcux_data spi_mcux_data_##n = { \
static struct lpspi_data lpspi_data_##n = { \
SPI_NXP_LPSPI_COMMON_DATA_INIT(n) \
.driver_data = &lpspi_##n##_driver_data, \
}; \
\
SPI_DEVICE_DT_INST_DEFINE(n, spi_mcux_init, NULL, &spi_mcux_data_##n, \
&spi_mcux_config_##n, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \
&spi_mcux_driver_api);
SPI_DEVICE_DT_INST_DEFINE(n, lpspi_init, NULL, &lpspi_data_##n, \
&lpspi_config_##n, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \
&lpspi_driver_api);

#define SPI_MCUX_LPSPI_INIT_IF_DMA(n) IF_DISABLED(SPI_NXP_LPSPI_HAS_DMAS(n), (LPSPI_INIT(n)))
#define SPI_LPSPI_INIT_IF_DMA(n) IF_DISABLED(SPI_NXP_LPSPI_HAS_DMAS(n), (LPSPI_INIT(n)))

#define SPI_MCUX_LPSPI_INIT(n) \
#define SPI_LPSPI_INIT(n) \
COND_CODE_1(CONFIG_SPI_MCUX_LPSPI_DMA, \
(SPI_MCUX_LPSPI_INIT_IF_DMA(n)), (LPSPI_INIT(n)))
(SPI_LPSPI_INIT_IF_DMA(n)), (LPSPI_INIT(n)))

DT_INST_FOREACH_STATUS_OKAY(SPI_MCUX_LPSPI_INIT)
DT_INST_FOREACH_STATUS_OKAY(SPI_LPSPI_INIT)
Loading
Loading