diff --git a/boards/arm/mimxrt685_evk/Kconfig.defconfig b/boards/arm/mimxrt685_evk/Kconfig.defconfig index b62e08515b4..f795691208b 100644 --- a/boards/arm/mimxrt685_evk/Kconfig.defconfig +++ b/boards/arm/mimxrt685_evk/Kconfig.defconfig @@ -32,4 +32,11 @@ config FXOS8700_DRDY_INT1 default y depends on FXOS8700_TRIGGER +if DMA_MCUX_LPC + +config HEAP_MEM_POOL_SIZE + default 4096 + +endif # DMA_MCUX_LPC + endif # BOARD_MIMXRT685_EVK diff --git a/boards/arm/mimxrt685_evk/mimxrt685_evk_cm33.dts b/boards/arm/mimxrt685_evk/mimxrt685_evk_cm33.dts index 51fc04b1a17..bbabd927627 100644 --- a/boards/arm/mimxrt685_evk/mimxrt685_evk_cm33.dts +++ b/boards/arm/mimxrt685_evk/mimxrt685_evk_cm33.dts @@ -99,6 +99,19 @@ arduino_spi: &flexcomm5 { status = "okay"; }; +&dma0 { + /* + * The total number of dma channels available is defined by + * FSL_FEATURE_DMA_NUMBER_OF_CHANNELS in the SoC features file. + * Since memory from the heap pool is allocated based on the number + * of DMA channels, set this property to as many channels is needed + * for the platform. Adjust HEAP_MEM_POOL_SIZE in case you need more + * memory. + */ + dma-channels = <20>; + status = "okay"; +}; + &user_button_1 { status = "okay"; }; diff --git a/boards/arm/mimxrt685_evk/mimxrt685_evk_cm33.yaml b/boards/arm/mimxrt685_evk/mimxrt685_evk_cm33.yaml index 25d007595d6..a21e8d950f1 100644 --- a/boards/arm/mimxrt685_evk/mimxrt685_evk_cm33.yaml +++ b/boards/arm/mimxrt685_evk/mimxrt685_evk_cm33.yaml @@ -18,6 +18,7 @@ supported: - arduino_i2c - arduino_spi - counter + - dma - gpio - hwinfo - i2c diff --git a/drivers/dma/CMakeLists.txt b/drivers/dma/CMakeLists.txt index fa153a737a6..01dc3432fa0 100644 --- a/drivers/dma/CMakeLists.txt +++ b/drivers/dma/CMakeLists.txt @@ -12,4 +12,5 @@ zephyr_library_sources_ifdef(CONFIG_DMA_NIOS2_MSGDMA dma_nios2_msgdma.c) zephyr_library_sources_ifdef(CONFIG_DMA_SAM0 dma_sam0.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE dma_handlers.c) zephyr_library_sources_ifdef(CONFIG_DMA_MCUX_EDMA dma_mcux_edma.c) +zephyr_library_sources_ifdef(CONFIG_DMA_MCUX_LPC dma_mcux_lpc.c) zephyr_library_sources_ifdef(CONFIG_DMA_PL330 dma_pl330.c) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 0467a94f3fe..57e78e6f3de 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -56,6 +56,8 @@ source "drivers/dma/Kconfig.sam0" source "drivers/dma/Kconfig.mcux_edma" +source "drivers/dma/Kconfig.mcux_lpc" + source "drivers/dma/Kconfig.dma_pl330" endif # DMA diff --git a/drivers/dma/Kconfig.mcux_lpc b/drivers/dma/Kconfig.mcux_lpc new file mode 100644 index 00000000000..56a059cab69 --- /dev/null +++ b/drivers/dma/Kconfig.mcux_lpc @@ -0,0 +1,20 @@ +# DMA configuration options + +# Copyright (c) 2020, NXP +# SPDX-License-Identifier: Apache-2.0 + +config DMA_MCUX_LPC + bool "Enable MCUX LPC DMAC driver" + depends on HAS_MCUX_LPC_DMA + help + DMA driver for MCUX LPC MCUs. + +if DMA_MCUX_LPC + +config DMA_LINK_QUEUE_SIZE + int "number of transfer descriptors in a queue for SG mode" + default 4 + help + number of transfer descriptors in a queue for SG mode + +endif # DMA_MCUX_LPC diff --git a/drivers/dma/dma_mcux_lpc.c b/drivers/dma/dma_mcux_lpc.c new file mode 100644 index 00000000000..0327bfea575 --- /dev/null +++ b/drivers/dma/dma_mcux_lpc.c @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2020 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Common part of DMA drivers for some NXP SoC. + */ +#include +#include +#include +#include +#include +#include +#include + +#define DT_DRV_COMPAT nxp_lpc_dma + +LOG_MODULE_REGISTER(dma_mcux_lpc, CONFIG_DMA_LOG_LEVEL); + +struct dma_mcux_lpc_config { + DMA_Type *base; + uint32_t num_of_channels; + void (*irq_config_func)(struct device *dev); +}; + +struct call_back { + dma_transfer_config_t *transferConfig; + dma_handle_t dma_handle; + struct device *dev; + void *user_data; + dma_callback_t dma_callback; + enum dma_channel_direction dir; + bool busy; + uint32_t channel_index; +}; + +struct dma_mcux_lpc_dma_data { + struct call_back *data_cb; + uint32_t *channel_index; + uint32_t num_channels_used; +}; + +#define DEV_CFG(dev) \ + ((const struct dma_mcux_lpc_config *const)(dev)->config) +#define DEV_DATA(dev) ((struct dma_mcux_lpc_dma_data *)dev->data) +#define DEV_BASE(dev) ((DMA_Type *)DEV_CFG(dev)->base) + +#define DEV_CHANNEL_DATA(dev, ch) \ + ((struct call_back *)(&(DEV_DATA(dev)->data_cb[ch]))) + +#define DEV_DMA_HANDLE(dev, ch) \ + ((dma_handle_t *)(&(DEV_CHANNEL_DATA(dev, ch)->dma_handle))) + +static void nxp_lpc_dma_callback(dma_handle_t *handle, void *param, + bool transferDone, uint32_t intmode) +{ + int ret = 1; + struct call_back *data = (struct call_back *)param; + uint32_t channel = handle->channel; + + if (transferDone) { + data->busy = false; + ret = 0; + } + + if (intmode == kDMA_IntError) { + DMA_AbortTransfer(handle); + } + + data->dma_callback(data->dev, data->user_data, channel, ret); +} + +/* Handles DMA interrupts and dispatches to the individual channel */ +static void dma_mcux_lpc_irq_handler(void *arg) +{ + struct device *dev = arg; + + DMA_IRQHandle(DEV_BASE(dev)); +/* + * Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store + * immediate overlapping exception return operation might vector + * to incorrect interrupt + */ +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} + +/* Configure a channel */ +static int dma_mcux_lpc_configure(struct device *dev, uint32_t channel, + struct dma_config *config) +{ + dma_handle_t *p_handle; + struct call_back *data; + struct dma_mcux_lpc_dma_data *dma_data = DEV_DATA(dev); + struct dma_block_config *block_config = config->head_block; + dma_transfer_type_t transfer_type; + uint32_t virtual_channel; + uint32_t total_dma_channels; + + if (NULL == dev || NULL == config) { + return -EINVAL; + } + + /* Check if have a free slot to store DMA channel data */ + if (dma_data->num_channels_used > DEV_CFG(dev)->num_of_channels) { + LOG_ERR("out of DMA channel %d", channel); + return -EINVAL; + } + +#if defined FSL_FEATURE_DMA_NUMBER_OF_CHANNELS + total_dma_channels = FSL_FEATURE_DMA_NUMBER_OF_CHANNELS; +#else + total_dma_channels = FSL_FEATURE_DMA_NUMBER_OF_CHANNELSn(DEV_BASE(dev)); +#endif + + /* Check if the dma channel number is valid */ + if (channel >= total_dma_channels) { + LOG_ERR("invalid DMA channel number %d", channel); + return -EINVAL; + } + + if (config->source_data_size != 4U && + config->source_data_size != 2U && + config->source_data_size != 1U) { + LOG_ERR("Source unit size error, %d", config->source_data_size); + return -EINVAL; + } + + if (config->dest_data_size != 4U && + config->dest_data_size != 2U && + config->dest_data_size != 1U) { + LOG_ERR("Dest unit size error, %d", config->dest_data_size); + return -EINVAL; + } + + switch (config->channel_direction) { + case MEMORY_TO_MEMORY: + transfer_type = kDMA_MemoryToMemory; + break; + case MEMORY_TO_PERIPHERAL: + transfer_type = kDMA_MemoryToPeripheral; + break; + case PERIPHERAL_TO_MEMORY: + transfer_type = kDMA_PeripheralToMemory; + break; + default: + LOG_ERR("not support transfer direction"); + return -EINVAL; + } + + /* If needed, allocate a slot to store dma channel data */ + if (dma_data->channel_index[channel] == -1) { + dma_data->channel_index[channel] = dma_data->num_channels_used; + dma_data->num_channels_used++; + } + + /* Get the slot number that has the dma channel data */ + virtual_channel = dma_data->channel_index[channel]; + /* dma channel data */ + p_handle = DEV_DMA_HANDLE(dev, virtual_channel); + data = DEV_CHANNEL_DATA(dev, virtual_channel); + + data->dir = config->channel_direction; + + if (data->busy) { + DMA_AbortTransfer(p_handle); + } + DMA_CreateHandle(p_handle, DEV_BASE(dev), channel); + DMA_SetCallback(p_handle, nxp_lpc_dma_callback, (void *)data); + + LOG_DBG("channel is %d", p_handle->channel); + + if (config->source_chaining_en && config->dest_chaining_en) { + LOG_DBG("link dma out 0 to channel %d", config->linked_channel); + /* Link DMA_OTRIG 0 to channel */ + INPUTMUX_AttachSignal(INPUTMUX, 0, config->linked_channel); + } + + /* Allocate the transfer config structures if needed */ + if (data->transferConfig == NULL) { + data->transferConfig = k_malloc(CONFIG_DMA_LINK_QUEUE_SIZE * + sizeof(dma_transfer_config_t)); + if (!data->transferConfig) { + LOG_ERR("HEAP_MEM_POOL_SIZE is too small"); + return -ENOMEM; + } + } + + if (block_config->source_gather_en || block_config->dest_scatter_en) { + if (config->block_count > CONFIG_DMA_LINK_QUEUE_SIZE) { + LOG_ERR("please config DMA_LINK_QUEUE_SIZE as %d", + config->block_count); + return -EINVAL; + } + int i = 0; + dma_transfer_config_t *next_transfer; + + while (block_config != NULL) { + /* Check if this last element in the chain */ + if (block_config->next_block == NULL) { + next_transfer = NULL; + } else { + next_transfer = &(data->transferConfig[i + 1]); + } + + DMA_PrepareTransfer( + &(data->transferConfig[i]), + (void *)block_config->source_address, + (void *)block_config->dest_address, + config->dest_data_size, + block_config->block_size, transfer_type, + (void *)next_transfer); + block_config = block_config->next_block; + i++; + } + + } else { + /* block_count shall be 1 */ + DMA_PrepareTransfer( + &(data->transferConfig[0]), + (void *)block_config->source_address, + (void *)block_config->dest_address, + config->dest_data_size, + block_config->block_size, transfer_type, + NULL); + } + DMA_SubmitTransfer(p_handle, &(data->transferConfig[0])); + + data->busy = false; + if (config->dma_callback) { + LOG_DBG("INSTALL call back on channel %d", channel); + data->user_data = config->user_data; + data->dma_callback = config->dma_callback; + data->dev = dev; + } + + return 0; +} + +static int dma_mcux_lpc_start(struct device *dev, uint32_t channel) +{ + uint32_t virtual_channel = DEV_DATA(dev)->channel_index[channel]; + struct call_back *data = DEV_CHANNEL_DATA(dev, virtual_channel); + + LOG_DBG("START TRANSFER"); + LOG_DBG("DMA CTRL 0x%x", DEV_BASE(dev)->CTRL); + data->busy = true; + DMA_StartTransfer(DEV_DMA_HANDLE(dev, virtual_channel)); + return 0; +} + +static int dma_mcux_lpc_stop(struct device *dev, uint32_t channel) +{ + uint32_t virtual_channel = DEV_DATA(dev)->channel_index[channel]; + struct call_back *data = DEV_CHANNEL_DATA(dev, virtual_channel); + + if (!data->busy) { + return 0; + } + DMA_AbortTransfer(DEV_DMA_HANDLE(dev, virtual_channel)); + data->busy = false; + return 0; +} + +static int dma_mcux_lpc_reload(struct device *dev, uint32_t channel, + uint32_t src, uint32_t dst, size_t size) +{ + uint32_t virtual_channel = DEV_DATA(dev)->channel_index[channel]; + struct call_back *data = DEV_CHANNEL_DATA(dev, virtual_channel); + + if (data->busy) { + DMA_AbortTransfer(DEV_DMA_HANDLE(dev, virtual_channel)); + } + return 0; +} + +static int dma_mcux_lpc_get_status(struct device *dev, uint32_t channel, + struct dma_status *status) +{ + uint32_t virtual_channel = DEV_DATA(dev)->channel_index[channel]; + struct call_back *data = DEV_CHANNEL_DATA(dev, virtual_channel); + + if (data->busy) { + status->busy = true; + status->pending_length = DMA_GetRemainingBytes(DEV_BASE(dev), channel); + } else { + status->busy = false; + status->pending_length = 0; + } + status->dir = data->dir; + LOG_DBG("DMA CR 0x%x", DEV_BASE(dev)->CTRL); + LOG_DBG("DMA INT 0x%x", DEV_BASE(dev)->INTSTAT); + + return 0; +} + +static int dma_mcux_lpc_init(struct device *dev) +{ + struct dma_mcux_lpc_dma_data *data = DEV_DATA(dev); + int size_channel_data; + int total_dma_channels; + + /* Array to store DMA channel data */ + size_channel_data = + sizeof(struct call_back) * DEV_CFG(dev)->num_of_channels; + data->data_cb = k_malloc(size_channel_data); + if (!data->data_cb) { + LOG_ERR("HEAP_MEM_POOL_SIZE is too small"); + return -ENOMEM; + } + + for (int i = 0; i < DEV_CFG(dev)->num_of_channels; i++) { + data->data_cb[i].transferConfig = NULL; + } + +#if defined FSL_FEATURE_DMA_NUMBER_OF_CHANNELS + total_dma_channels = FSL_FEATURE_DMA_NUMBER_OF_CHANNELS; +#else + total_dma_channels = FSL_FEATURE_DMA_NUMBER_OF_CHANNELSn(DEV_BASE(dev)); +#endif + /* + * This array is used to hold the index associated with the array + * holding channel data + */ + data->channel_index = k_malloc(sizeof(uint32_t) * total_dma_channels); + if (!data->channel_index) { + LOG_ERR("HEAP_MEM_POOL_SIZE is too small"); + return -ENOMEM; + } + + /* + * Initialize to -1 to indicate dma channel does not have a slot + * assigned to store dma channel data + */ + for (int i = 0; i < total_dma_channels; i++) { + data->channel_index[i] = -1; + } + + data->num_channels_used = 0; + + + DMA_Init(DEV_BASE(dev)); + INPUTMUX_Init(INPUTMUX); + + return 0; +} + +static const struct dma_driver_api dma_mcux_lpc_api = { + .config = dma_mcux_lpc_configure, + .start = dma_mcux_lpc_start, + .stop = dma_mcux_lpc_stop, + .reload = dma_mcux_lpc_reload, + .get_status = dma_mcux_lpc_get_status, +}; + +#define DMA_MCUX_LPC_CONFIG_FUNC(n) \ + static void dma_mcux_lpc_config_func_##n(struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + dma_mcux_lpc_irq_handler, DEVICE_GET(dma_mcux_lpc_##n), 0);\ + \ + irq_enable(DT_INST_IRQN(n)); \ + } +#define DMA_MCUX_LPC_IRQ_CFG_FUNC_INIT(n) \ + .irq_config_func = dma_mcux_lpc_config_func_##n +#define DMA_MCUX_LPC_INIT_CFG(n) \ + DMA_MCUX_LPC_DECLARE_CFG(n, \ + DMA_MCUX_LPC_IRQ_CFG_FUNC_INIT(n)) + +#define DMA_MCUX_LPC_DECLARE_CFG(n, IRQ_FUNC_INIT) \ +static const struct dma_mcux_lpc_config dma_##n##_config = { \ + .base = (DMA_Type *)DT_INST_REG_ADDR(n), \ + .num_of_channels = DT_INST_PROP(n, dma_channels), \ + IRQ_FUNC_INIT \ +} + +#define DMA_INIT(n) \ + \ + static const struct dma_mcux_lpc_config dma_##n##_config;\ + \ + static struct dma_mcux_lpc_dma_data dma_data_##n = { \ + .data_cb = NULL, \ + }; \ + \ + DEVICE_AND_API_INIT(dma_mcux_lpc_##n, DT_INST_LABEL(n), \ + &dma_mcux_lpc_init, \ + &dma_data_##n, &dma_##n##_config,\ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,\ + &dma_mcux_lpc_api); \ + \ + DMA_MCUX_LPC_CONFIG_FUNC(n) \ + \ + DMA_MCUX_LPC_INIT_CFG(n); + +DT_INST_FOREACH_STATUS_OKAY(DMA_INIT) diff --git a/dts/arm/nxp/nxp_rt6xx_common.dtsi b/dts/arm/nxp/nxp_rt6xx_common.dtsi index 1b65df1fec1..b322cd5942e 100644 --- a/dts/arm/nxp/nxp_rt6xx_common.dtsi +++ b/dts/arm/nxp/nxp_rt6xx_common.dtsi @@ -154,6 +154,24 @@ #address-cells = <1>; #size-cells = <0>; }; + + dma0: dma-controller@104000 { + compatible = "nxp,lpc-dma"; + reg = <0x104000 0x1000>; + interrupts = <1 0>; + label = "DMA_0"; + status = "disabled"; + #dma-cells = <0>; + }; + + dma1: dma-controller@105000 { + compatible = "nxp,lpc-dma"; + reg = <0x105000 0x1000>; + interrupts = <54 0>; + label = "DMA_1"; + status = "disabled"; + #dma-cells = <0>; + }; }; &nvic { diff --git a/dts/bindings/dma/nxp,lpc-dma.yaml b/dts/bindings/dma/nxp,lpc-dma.yaml new file mode 100644 index 00000000000..4ebad1ce2cc --- /dev/null +++ b/dts/bindings/dma/nxp,lpc-dma.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2020, NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP LPC DMA controller + +compatible: "nxp,lpc-dma" + +include: dma-controller.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + dma-channels: + required: true diff --git a/modules/Kconfig.mcux b/modules/Kconfig.mcux index 9e8b98a7f72..a2e4203f9ba 100644 --- a/modules/Kconfig.mcux +++ b/modules/Kconfig.mcux @@ -204,6 +204,11 @@ config HAS_MCUX_EDMA help Set if the EDMA module is present on the SoC. +config HAS_MCUX_LPC_DMA + bool + help + Set if the DMA module is present on the SoC. + config HAS_MCUX_RDC bool help diff --git a/soc/arm/nxp_imx/rt6xx/Kconfig.defconfig.mimxrt685_cm33 b/soc/arm/nxp_imx/rt6xx/Kconfig.defconfig.mimxrt685_cm33 index be664f02c29..eeda2af8ea7 100644 --- a/soc/arm/nxp_imx/rt6xx/Kconfig.defconfig.mimxrt685_cm33 +++ b/soc/arm/nxp_imx/rt6xx/Kconfig.defconfig.mimxrt685_cm33 @@ -28,4 +28,8 @@ config SPI_MCUX_FLEXCOMM default y if HAS_MCUX_FLEXCOMM depends on SPI +config DMA_MCUX_LPC + default y + depends on DMA + endif # SOC_MIMXRT685S_CM33 diff --git a/soc/arm/nxp_imx/rt6xx/Kconfig.soc b/soc/arm/nxp_imx/rt6xx/Kconfig.soc index 687aaab602a..0aa3ce466b5 100644 --- a/soc/arm/nxp_imx/rt6xx/Kconfig.soc +++ b/soc/arm/nxp_imx/rt6xx/Kconfig.soc @@ -18,6 +18,7 @@ config SOC_MIMXRT685S_CM33 select HAS_MCUX select HAS_MCUX_FLEXCOMM select HAS_MCUX_CACHE + select HAS_MCUX_LPC_DMA select INIT_SYS_PLL endchoice diff --git a/west.yml b/west.yml index 2ef179017e0..76fb64170b0 100644 --- a/west.yml +++ b/west.yml @@ -98,7 +98,7 @@ manifest: revision: 1c4fdba512b268033a4cf926bddd323866c3261a path: tools/net-tools - name: hal_nxp - revision: 727b36cfe9e2f2aa5eb88d608e6334c8d548cee9 + revision: a501b0477ceceaa0abcba4e1fea0f64bbe8b6bae path: modules/hal/nxp - name: open-amp revision: 724f7e2a4519d7e1d40ef330042682dea950c991