diff --git a/drivers/spi/spi_nxp_lpspi/Kconfig b/drivers/spi/spi_nxp_lpspi/Kconfig index 8ff926c931bc8..aabb4d51ce7e6 100644 --- a/drivers/spi/spi_nxp_lpspi/Kconfig +++ b/drivers/spi/spi_nxp_lpspi/Kconfig @@ -1,4 +1,4 @@ -# Copyright 2018, 2024 NXP +# Copyright 2018, 2024-2025 NXP # SPDX-License-Identifier: Apache-2.0 config SPI_MCUX_LPSPI @@ -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 diff --git a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c index 88dd3c33e1ce5..629f2c8574263 100644 --- a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c +++ b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi.c @@ -7,7 +7,7 @@ #define DT_DRV_COMPAT nxp_lpspi #include -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" @@ -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 */ @@ -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); @@ -69,7 +69,7 @@ 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); @@ -77,7 +77,7 @@ static inline void lpspi_handle_rx_irq(const struct device *dev) 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); @@ -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; @@ -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); @@ -130,11 +130,11 @@ 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); @@ -142,8 +142,8 @@ static void lpspi_fill_tx_fifo_nop(const struct device *dev) 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; @@ -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; } @@ -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); } @@ -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; @@ -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; @@ -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) { @@ -281,7 +280,7 @@ 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) { @@ -289,7 +288,7 @@ static int spi_mcux_transceive_sync(const struct device *dev, const struct spi_c } #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) @@ -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); @@ -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) diff --git a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c index 4d8450b1cae90..7f65b2c9cd0ea 100644 --- a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c +++ b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_common.c @@ -5,9 +5,53 @@ */ #include -LOG_MODULE_REGISTER(spi_mcux_lpspi_common, CONFIG_SPI_LOG_LEVEL); +LOG_MODULE_REGISTER(spi_lpspi_common, CONFIG_SPI_LOG_LEVEL); #include "spi_nxp_lpspi_priv.h" +#include + +#if defined(LPSPI_RSTS) || defined(LPSPI_CLOCKS) +static LPSPI_Type *const lpspi_bases[] = LPSPI_BASE_PTRS; +#endif + +#ifdef LPSPI_RSTS +static const reset_ip_name_t lpspi_resets[] = LPSPI_RSTS; + +static inline reset_ip_name_t lpspi_get_reset(LPSPI_Type *const base) +{ + reset_ip_name_t rst = -1; /* invalid initial value */ + + ARRAY_FOR_EACH(lpspi_bases, idx) { + if (lpspi_bases[idx] == base) { + rst = lpspi_resets[idx]; + break; + } + } + + __ASSERT_NO_MSG(rst != -1); + return rst; + +} +#endif + +#ifdef LPSPI_CLOCKS +static const clock_ip_name_t lpspi_clocks[] = LPSPI_CLOCKS; + +static inline clock_ip_name_t lpspi_get_clock(LPSPI_Type *const base) +{ + clock_ip_name_t clk = -1; /* invalid initial value */ + + ARRAY_FOR_EACH(lpspi_bases, idx) { + if (lpspi_bases[idx] == base) { + clk = lpspi_clocks[idx]; + break; + } + } + + __ASSERT_NO_MSG(clk != -1); + return clk; +} +#endif void lpspi_wait_tx_fifo_empty(const struct device *dev) { @@ -17,38 +61,23 @@ void lpspi_wait_tx_fifo_empty(const struct device *dev) } } -int spi_mcux_release(const struct device *dev, const struct spi_config *spi_cfg) +int spi_lpspi_release(const struct device *dev, const struct spi_config *spi_cfg) { - struct spi_mcux_data *data = dev->data; + struct lpspi_data *data = dev->data; spi_context_unlock_unconditionally(&data->ctx); return 0; } -int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cfg) +static inline int lpspi_validate_xfer_args(const struct spi_config *spi_cfg) { - const struct spi_mcux_config *config = dev->config; - struct spi_mcux_data *data = dev->data; - struct spi_context *ctx = &data->ctx; - LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base); uint32_t word_size = SPI_WORD_SIZE_GET(spi_cfg->operation); - bool configured = ctx->config != NULL; - lpspi_master_config_t master_config; - uint32_t clock_freq; - int ret; - - /* fast path to avoid reconfigure */ - /* TODO: S32K3 errata ERR050456 requiring module reset before every transfer, - * investigate alternative workaround so we don't have this latency for S32. - */ - if (spi_context_configured(ctx, spi_cfg) && !IS_ENABLED(CONFIG_SOC_FAMILY_NXP_S32)) { - return 0; - } + uint32_t pcs = spi_cfg->slave; if (spi_cfg->operation & SPI_HALF_DUPLEX) { /* the IP DOES support half duplex, need to implement driver support */ - LOG_ERR("Half-duplex not supported"); + LOG_WRN("Half-duplex not supported"); return -ENOTSUP; } @@ -60,22 +89,49 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf * Minimum hardware word size is 2. Since this driver is intended to work * for 32 bit platforms, and 64 bits is max size, then only 33 and 1 are invalid. */ - LOG_ERR("Word size %d not allowed", word_size); + LOG_WRN("Word size %d not allowed", word_size); return -EINVAL; } - if (spi_cfg->slave > (LPSPI_CHIP_SELECT_COUNT - 1)) { - LOG_ERR("Peripheral %d select exceeds max %d", spi_cfg->slave, - LPSPI_CHIP_SELECT_COUNT - 1); + if (pcs > LPSPI_CHIP_SELECT_COUNT - 1) { + LOG_WRN("Peripheral %d select exceeds max %d", pcs, LPSPI_CHIP_SELECT_COUNT - 1); return -EINVAL; } + return 0; +} + +int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cfg) +{ + const struct lpspi_config *config = dev->config; + struct lpspi_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + bool already_configured = spi_context_configured(ctx, spi_cfg); + LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base); + uint32_t word_size = SPI_WORD_SIZE_GET(spi_cfg->operation); + lpspi_master_config_t master_config; + uint32_t clock_freq; + int ret; + + /* fast path to avoid reconfigure */ + /* TODO: S32K3 errata ERR050456 requiring module reset before every transfer, + * investigate alternative workaround so we don't have this latency for S32. + */ + if (already_configured && !IS_ENABLED(CONFIG_SOC_FAMILY_NXP_S32)) { + return 0; + } + + ret = lpspi_validate_xfer_args(spi_cfg); + if (ret) { + return ret; + } + ret = clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_freq); if (ret) { return ret; } - if (configured) { + if (already_configured) { /* Setting the baud rate in LPSPI_MasterInit requires module to be disabled. Only * disable if already configured, otherwise the clock is not enabled and the * CR register cannot be written. @@ -115,8 +171,8 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf master_config.pcsActiveHighOrLow = (spi_cfg->operation & SPI_CS_ACTIVE_HIGH) ? kLPSPI_PcsActiveHigh : kLPSPI_PcsActiveLow; master_config.pinCfg = config->data_pin_config; - master_config.dataOutConfig = config->output_config ? kLpspiDataOutTristate : - kLpspiDataOutRetained; + master_config.dataOutConfig = config->tristate_output ? kLpspiDataOutTristate : + kLpspiDataOutRetained; LPSPI_MasterInit(base, &master_config, clock_freq); LPSPI_SetDummyData(base, 0); @@ -128,11 +184,22 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf return 0; } +static void lpspi_module_system_init(LPSPI_Type *base) +{ +#ifdef LPSPI_CLOCKS + CLOCK_EnableClock(lpspi_get_clock(base)); +#endif + +#ifdef LPSPI_RSTS + RESET_ReleasePeripheralReset(lpspi_get_reset(base)); +#endif +} + int spi_nxp_init_common(const struct device *dev) { LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base); - 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; int err = 0; DEVICE_MMIO_NAMED_MAP(dev, reg_base, K_MEM_CACHE_NONE | K_MEM_DIRECT_MAP); @@ -144,6 +211,8 @@ int spi_nxp_init_common(const struct device *dev) return -ENODEV; } + lpspi_module_system_init(base); + err = spi_context_cs_configure_all(&data->ctx); if (err < 0) { return err; diff --git a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_dma.c b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_dma.c index 0b204f486910e..061d0bd7dc030 100644 --- a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_dma.c +++ b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_dma.c @@ -7,7 +7,7 @@ #define DT_DRV_COMPAT nxp_lpspi #include -LOG_MODULE_REGISTER(spi_mcux_lpspi_dma, CONFIG_SPI_LOG_LEVEL); +LOG_MODULE_REGISTER(spi_lpspi_dma, CONFIG_SPI_LOG_LEVEL); #include #include "spi_nxp_lpspi_priv.h" @@ -30,7 +30,7 @@ struct spi_nxp_dma_data { struct spi_dma_stream dma_tx; }; -static struct dma_block_config *spi_mcux_dma_common_load(struct spi_dma_stream *stream, +static struct dma_block_config *lpspi_dma_common_load(struct spi_dma_stream *stream, const struct device *dev, const uint8_t *buf, size_t len) { @@ -47,13 +47,13 @@ static struct dma_block_config *spi_mcux_dma_common_load(struct spi_dma_stream * return blk_cfg; } -static int spi_mcux_dma_tx_load(const struct device *dev, const uint8_t *buf, size_t len) +static int lpspi_dma_tx_load(const struct device *dev, const uint8_t *buf, size_t len) { 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 spi_nxp_dma_data *dma_data = (struct spi_nxp_dma_data *)data->driver_data; struct spi_dma_stream *stream = &dma_data->dma_tx; - struct dma_block_config *blk_cfg = spi_mcux_dma_common_load(stream, dev, buf, len); + struct dma_block_config *blk_cfg = lpspi_dma_common_load(stream, dev, buf, len); if (buf == NULL) { /* pretend that nop value comes from peripheral so dma doesn't move source */ @@ -64,19 +64,18 @@ static int spi_mcux_dma_tx_load(const struct device *dev, const uint8_t *buf, si stream->dma_cfg.channel_direction = MEMORY_TO_PERIPHERAL; } - /* Dest is always LPSPI tx fifo */ - blk_cfg->dest_address = LPSPI_GetTxRegisterAddress(base); + blk_cfg->dest_address = (uint32_t) &(base->TDR); return dma_config(stream->dma_dev, stream->channel, &stream->dma_cfg); } -static int spi_mcux_dma_rx_load(const struct device *dev, uint8_t *buf, size_t len) +static int lpspi_dma_rx_load(const struct device *dev, uint8_t *buf, size_t len) { 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 spi_nxp_dma_data *dma_data = (struct spi_nxp_dma_data *)data->driver_data; struct spi_dma_stream *stream = &dma_data->dma_rx; - struct dma_block_config *blk_cfg = spi_mcux_dma_common_load(stream, dev, buf, len); + struct dma_block_config *blk_cfg = lpspi_dma_common_load(stream, dev, buf, len); if (buf == NULL) { /* pretend it is peripheral xfer so DMA just xfer to dummy buf */ @@ -87,14 +86,14 @@ static int spi_mcux_dma_rx_load(const struct device *dev, uint8_t *buf, size_t l blk_cfg->dest_address = (uint32_t)buf; } - blk_cfg->source_address = LPSPI_GetRxRegisterAddress(base); + blk_cfg->source_address = (uint32_t) &(base->RDR); return dma_config(stream->dma_dev, stream->channel, &stream->dma_cfg); } -static inline int spi_mcux_dma_rxtx_load(const struct device *dev) +static inline int lpspi_dma_rxtx_load(const struct device *dev) { - struct spi_mcux_data *data = dev->data; + struct lpspi_data *data = dev->data; struct spi_nxp_dma_data *dma_data = (struct spi_nxp_dma_data *)data->driver_data; struct spi_dma_stream *rx = &dma_data->dma_rx; struct spi_dma_stream *tx = &dma_data->dma_tx; @@ -102,12 +101,20 @@ static inline int spi_mcux_dma_rxtx_load(const struct device *dev) size_t next_chunk_size = spi_context_max_continuous_chunk(ctx); int ret = 0; - ret = spi_mcux_dma_tx_load(dev, ctx->tx_buf, next_chunk_size); + if (next_chunk_size == 0) { + /* In case both buffers are 0 length, we should not even be here + * and attempting to set up a DMA transfer like this will cause + * errors that lock up the system in some cases with eDMA. + */ + return -ENODATA; + } + + ret = lpspi_dma_tx_load(dev, ctx->tx_buf, next_chunk_size); if (ret != 0) { return ret; } - ret = spi_mcux_dma_rx_load(dev, ctx->rx_buf, next_chunk_size); + ret = lpspi_dma_rx_load(dev, ctx->rx_buf, next_chunk_size); if (ret != 0) { return ret; } @@ -120,9 +127,9 @@ static inline int spi_mcux_dma_rxtx_load(const struct device *dev) return dma_start(tx->dma_dev, tx->channel); } -static int spi_mcux_dma_next_fill(const struct device *dev) +static int lpspi_dma_next_fill(const struct device *dev) { - struct spi_mcux_data *data = (struct spi_mcux_data *)dev->data; + struct lpspi_data *data = (struct lpspi_data *)dev->data; struct spi_nxp_dma_data *dma_data = (struct spi_nxp_dma_data *)data->driver_data; struct spi_dma_stream *rx = &dma_data->dma_rx; struct spi_dma_stream *tx = &dma_data->dma_tx; @@ -130,14 +137,14 @@ static int spi_mcux_dma_next_fill(const struct device *dev) rx->chunk_done = false; tx->chunk_done = false; - return spi_mcux_dma_rxtx_load(dev); + return lpspi_dma_rxtx_load(dev); } static void spi_mcux_dma_callback(const struct device *dev, void *arg, uint32_t channel, int status) { const struct device *spi_dev = arg; LPSPI_Type *base = (LPSPI_Type *)DEVICE_MMIO_NAMED_GET(spi_dev, reg_base); - struct spi_mcux_data *data = (struct spi_mcux_data *)spi_dev->data; + struct lpspi_data *data = (struct lpspi_data *)spi_dev->data; struct spi_nxp_dma_data *dma_data = (struct spi_nxp_dma_data *)data->driver_data; struct spi_dma_stream *rx = &dma_data->dma_rx; struct spi_dma_stream *tx = &dma_data->dma_tx; @@ -172,8 +179,10 @@ static void spi_mcux_dma_callback(const struct device *dev, void *arg, uint32_t return; } - while ((IS_ENABLED(CONFIG_SOC_FAMILY_NXP_IMXRT) || IS_ENABLED(CONFIG_SOC_FAMILY_KINETIS)) && - (LPSPI_GetStatusFlags(base) & kLPSPI_ModuleBusyFlag)) { + + while ((IS_ENABLED(CONFIG_SOC_FAMILY_NXP_IMXRT) || + IS_ENABLED(CONFIG_SOC_FAMILY_KINETIS)) && + (base->SR & LPSPI_SR_MBF_MASK)) { /* wait until module is idle */ } @@ -181,7 +190,7 @@ static void spi_mcux_dma_callback(const struct device *dev, void *arg, uint32_t goto done; } - status = spi_mcux_dma_next_fill(spi_dev); + status = lpspi_dma_next_fill(spi_dev); if (status) { goto error; } @@ -190,12 +199,13 @@ static void spi_mcux_dma_callback(const struct device *dev, void *arg, uint32_t error: LOG_ERR("DMA callback error with channel %d err %d.", channel, status); done: - LPSPI_DisableDMA(base, kLPSPI_TxDmaEnable | kLPSPI_RxDmaEnable); + base->DER &= ~(LPSPI_DER_TDDE_MASK | LPSPI_DER_RDDE_MASK); base->TCR &= ~LPSPI_TCR_CONT_MASK; lpspi_wait_tx_fifo_empty(spi_dev); spi_context_cs_control(ctx, false); - LPSPI_FlushFifo(base, true, true); + base->CR |= LPSPI_CR_RTF_MASK | LPSPI_CR_RRF_MASK; spi_context_complete(ctx, spi_dev, status); + spi_context_release(ctx, status); } static int transceive_dma(const struct device *dev, const struct spi_config *spi_cfg, @@ -203,7 +213,7 @@ static int transceive_dma(const struct device *dev, const struct spi_config *spi 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 spi_context *ctx = &data->ctx; int ret; @@ -216,22 +226,29 @@ static int transceive_dma(const struct device *dev, const struct spi_config *spi spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1); + ret = lpspi_dma_next_fill(dev); + if (ret == -ENODATA) { + /* No transfer to do? So just exit */ + ret = 0; + goto out; + } else if (ret) { + goto out; + } + if (!(IS_ENABLED(CONFIG_SOC_FAMILY_NXP_IMXRT) || IS_ENABLED(CONFIG_SOC_FAMILY_KINETIS))) { base->TCR |= LPSPI_TCR_CONT_MASK; } spi_context_cs_control(ctx, true); - ret = spi_mcux_dma_next_fill(dev); - if (ret) { - goto out; - } + base->CR |= LPSPI_CR_RTF_MASK | LPSPI_CR_RRF_MASK; - LPSPI_FlushFifo(base, true, true); - - LPSPI_EnableDMA(base, kLPSPI_TxDmaEnable | kLPSPI_RxDmaEnable); + base->DER |= LPSPI_DER_TDDE_MASK | LPSPI_DER_RDDE_MASK; ret = spi_context_wait_for_completion(ctx); + if (ret >= 0) { + return ret; + } out: spi_context_release(ctx, ret); return ret; @@ -249,7 +266,7 @@ static int lpspi_dma_dev_ready(const struct device *dma_dev) static int spi_mcux_dma_init(const struct device *dev) { - struct spi_mcux_data *data = dev->data; + struct lpspi_data *data = dev->data; struct spi_nxp_dma_data *dma_data = (struct spi_nxp_dma_data *)data->driver_data; int err = 0; @@ -285,7 +302,7 @@ static int spi_nxp_dma_transceive_async(const struct device *dev, const struct s } #endif /* CONFIG_SPI_ASYNC */ -static DEVICE_API(spi, spi_mcux_driver_api) = { +static DEVICE_API(spi, lpspi_dma_driver_api) = { .transceive = spi_nxp_dma_transceive_sync, #ifdef CONFIG_SPI_ASYNC .transceive_async = spi_nxp_dma_transceive_async, @@ -293,7 +310,7 @@ static DEVICE_API(spi, spi_mcux_driver_api) = { #ifdef CONFIG_SPI_RTIO .iodev_submit = spi_rtio_iodev_default_submit, #endif - .release = spi_mcux_release, + .release = spi_lpspi_release, }; static void lpspi_isr(const struct device *dev) @@ -323,16 +340,16 @@ static void lpspi_isr(const struct device *dev) #define LPSPI_DMA_INIT(n) \ SPI_NXP_LPSPI_COMMON_INIT(n) \ - SPI_MCUX_LPSPI_CONFIG_INIT(n) \ + SPI_LPSPI_CONFIG_INIT(n) \ \ static struct spi_nxp_dma_data lpspi_dma_data##n = {SPI_DMA_CHANNELS(n)}; \ \ - static struct spi_mcux_data spi_mcux_data_##n = {.driver_data = &lpspi_dma_data##n, \ + static struct lpspi_data lpspi_data_##n = {.driver_data = &lpspi_dma_data##n, \ SPI_NXP_LPSPI_COMMON_DATA_INIT(n)}; \ \ - SPI_DEVICE_DT_INST_DEFINE(n, spi_mcux_dma_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, spi_mcux_dma_init, NULL, &lpspi_data_##n, \ + &lpspi_config_##n, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ + &lpspi_dma_driver_api); #define SPI_NXP_LPSPI_DMA_INIT(n) IF_ENABLED(SPI_NXP_LPSPI_HAS_DMAS(n), (LPSPI_DMA_INIT(n))) diff --git a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h index e02db078028f8..2054000de2e66 100644 --- a/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h +++ b/drivers/spi/spi_nxp_lpspi/spi_nxp_lpspi_priv.h @@ -1,5 +1,5 @@ /* - * Copyright 2018, 2024 NXP + * Copyright 2018, 2024-2025 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "../spi_context.h" @@ -16,23 +17,19 @@ #include #endif -#include - /* If any hardware revisions change this, make it into a DT property. * DONT'T make #ifdefs here by platform. */ #define LPSPI_CHIP_SELECT_COUNT 4 #define LPSPI_MIN_FRAME_SIZE_BITS 8 -/* Required by DEVICE_MMIO_NAMED_* macros */ -#define DEV_CFG(_dev) ((const struct spi_mcux_config *)(_dev)->config) -#define DEV_DATA(_dev) ((struct spi_mcux_data *)(_dev)->data) +#define LPSPI_INTERRUPT_BITS GENMASK(8, 13) -/* flag for SDK API for master transfers */ -#define LPSPI_MASTER_XFER_CFG_FLAGS(slave) \ - kLPSPI_MasterPcsContinuous | (slave << LPSPI_MASTER_PCS_SHIFT) +/* Required by DEVICE_MMIO_NAMED_* macros */ +#define DEV_CFG(_dev) ((const struct lpspi_config *)(_dev)->config) +#define DEV_DATA(_dev) ((struct lpspi_data *)(_dev)->data) -struct spi_mcux_config { +struct lpspi_config { DEVICE_MMIO_NAMED_ROM(reg_base); const struct device *clock_dev; clock_control_subsys_t clock_subsys; @@ -41,14 +38,14 @@ struct spi_mcux_config { uint32_t sck_pcs_delay; uint32_t transfer_delay; const struct pinctrl_dev_config *pincfg; - lpspi_pin_config_t data_pin_config; - bool output_config; + uint8_t data_pin_config; + bool tristate_output; uint8_t tx_fifo_size; uint8_t rx_fifo_size; uint8_t irqn; }; -struct spi_mcux_data { +struct lpspi_data { DEVICE_MMIO_NAMED_RAM(reg_base); const struct device *dev; struct spi_context ctx; @@ -56,7 +53,7 @@ struct spi_mcux_data { size_t transfer_len; }; -/* common configure function that verifies spi_cfg validity and set up configuration parameters */ +/* verifies spi_cfg validity and set up configuration of hardware for xfer */ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cfg); /* Does these things: @@ -69,36 +66,33 @@ int spi_mcux_configure(const struct device *dev, const struct spi_config *spi_cf int spi_nxp_init_common(const struct device *dev); /* common api function for now */ -int spi_mcux_release(const struct device *dev, const struct spi_config *spi_cfg); +int spi_lpspi_release(const struct device *dev, const struct spi_config *spi_cfg); void lpspi_wait_tx_fifo_empty(const struct device *dev); -/* Argument to MCUX SDK IRQ handler */ -#define LPSPI_IRQ_HANDLE_ARG COND_CODE_1(CONFIG_NXP_LP_FLEXCOMM, (LPSPI_GetInstance(base)), (base)) - -#define SPI_MCUX_LPSPI_IRQ_FUNC_LP_FLEXCOMM(n) \ +#define SPI_LPSPI_IRQ_FUNC_LP_FLEXCOMM(n) \ nxp_lp_flexcomm_setirqhandler(DEVICE_DT_GET(DT_INST_PARENT(n)), DEVICE_DT_INST_GET(n), \ LP_FLEXCOMM_PERIPH_LPSPI, lpspi_isr); -#define SPI_MCUX_LPSPI_IRQ_FUNC_DISTINCT(n) \ +#define SPI_LPSPI_IRQ_FUNC_DISTINCT(n) \ IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), lpspi_isr, DEVICE_DT_INST_GET(n), \ 0); \ irq_enable(DT_INST_IRQN(n)); -#define SPI_MCUX_LPSPI_IRQ_FUNC(n) COND_CODE_1(DT_NODE_HAS_COMPAT(DT_INST_PARENT(n), \ - nxp_lp_flexcomm), \ - (SPI_MCUX_LPSPI_IRQ_FUNC_LP_FLEXCOMM(n)), \ - (SPI_MCUX_LPSPI_IRQ_FUNC_DISTINCT(n))) +#define SPI_LPSPI_IRQ_FUNC(n) COND_CODE_1(DT_NODE_HAS_COMPAT(DT_INST_PARENT(n), \ + nxp_lp_flexcomm), \ + (SPI_LPSPI_IRQ_FUNC_LP_FLEXCOMM(n)), \ + (SPI_LPSPI_IRQ_FUNC_DISTINCT(n))) #define LPSPI_IRQN(n) COND_CODE_1(DT_NODE_HAS_COMPAT(DT_INST_PARENT(n), nxp_lp_flexcomm), \ (DT_IRQN(DT_INST_PARENT(n))), (DT_INST_IRQN(n))) -#define SPI_MCUX_LPSPI_CONFIG_INIT(n) \ - static const struct spi_mcux_config spi_mcux_config_##n = { \ +#define SPI_LPSPI_CONFIG_INIT(n) \ + static const struct lpspi_config lpspi_config_##n = { \ DEVICE_MMIO_NAMED_ROM_INIT(reg_base, DT_DRV_INST(n)), \ .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ .clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), \ - .irq_config_func = spi_mcux_config_func_##n, \ + .irq_config_func = lpspi_config_func_##n, \ .pcs_sck_delay = UTIL_AND(DT_INST_NODE_HAS_PROP(n, pcs_sck_delay), \ DT_INST_PROP(n, pcs_sck_delay)), \ .sck_pcs_delay = UTIL_AND(DT_INST_NODE_HAS_PROP(n, sck_pcs_delay), \ @@ -106,8 +100,8 @@ void lpspi_wait_tx_fifo_empty(const struct device *dev); .transfer_delay = UTIL_AND(DT_INST_NODE_HAS_PROP(n, transfer_delay), \ DT_INST_PROP(n, transfer_delay)), \ .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ - .data_pin_config = DT_INST_ENUM_IDX(n, data_pin_config), \ - .output_config = DT_INST_PROP(n, tristate_output), \ + .data_pin_config = (uint8_t)DT_INST_ENUM_IDX(n, data_pin_config), \ + .tristate_output = DT_INST_PROP(n, tristate_output), \ .rx_fifo_size = (uint8_t)DT_INST_PROP(n, rx_fifo_size), \ .tx_fifo_size = (uint8_t)DT_INST_PROP(n, tx_fifo_size), \ .irqn = (uint8_t)LPSPI_IRQN(n), \ @@ -116,14 +110,14 @@ void lpspi_wait_tx_fifo_empty(const struct device *dev); #define SPI_NXP_LPSPI_COMMON_INIT(n) \ PINCTRL_DT_INST_DEFINE(n); \ \ - static void spi_mcux_config_func_##n(const struct device *dev) \ + static void lpspi_config_func_##n(const struct device *dev) \ { \ - SPI_MCUX_LPSPI_IRQ_FUNC(n) \ + SPI_LPSPI_IRQ_FUNC(n) \ } #define SPI_NXP_LPSPI_COMMON_DATA_INIT(n) \ - SPI_CONTEXT_INIT_LOCK(spi_mcux_data_##n, ctx), \ - SPI_CONTEXT_INIT_SYNC(spi_mcux_data_##n, ctx), \ + SPI_CONTEXT_INIT_LOCK(lpspi_data_##n, ctx), \ + SPI_CONTEXT_INIT_SYNC(lpspi_data_##n, ctx), \ SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx) #define SPI_NXP_LPSPI_HAS_DMAS(n) \