From 6df4a3d530bda8cff12e4f6ea041b22379fe6acf Mon Sep 17 00:00:00 2001 From: Balsundar Ponnusamy Date: Sat, 12 Aug 2023 08:20:47 +0000 Subject: [PATCH 1/4] drivers: dma: add dma driver for designware axi DMA controller Adding dma driver source for designware axi dma controller Signed-off-by: Balsundar Ponnusamy --- drivers/dma/CMakeLists.txt | 1 + drivers/dma/Kconfig | 2 + drivers/dma/Kconfig.dw_axi_dmac | 53 + drivers/dma/dma_dw_axi.c | 912 ++++++++++++++++++ dts/bindings/dma/snps,designware-dma-axi.yaml | 36 + 5 files changed, 1004 insertions(+) create mode 100644 drivers/dma/Kconfig.dw_axi_dmac create mode 100644 drivers/dma/dma_dw_axi.c create mode 100644 dts/bindings/dma/snps,designware-dma-axi.yaml diff --git a/drivers/dma/CMakeLists.txt b/drivers/dma/CMakeLists.txt index 5b2a2ea3353c..a2710f0a2571 100644 --- a/drivers/dma/CMakeLists.txt +++ b/drivers/dma/CMakeLists.txt @@ -41,3 +41,4 @@ zephyr_library_sources_ifdef(CONFIG_DMA_SMARTBOND dma_smartbond.c) zephyr_library_sources_ifdef(CONFIG_DMA_NXP_SOF_HOST_DMA dma_nxp_sof_host_dma.c) zephyr_library_sources_ifdef(CONFIG_DMA_EMUL dma_emul.c) zephyr_library_sources_ifdef(CONFIG_DMA_NXP_EDMA dma_nxp_edma.c) +zephyr_library_sources_ifdef(CONFIG_DMA_DW_AXI dma_dw_axi.c) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 7f584dae57bc..4a99e11cac3b 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -76,4 +76,6 @@ source "drivers/dma/Kconfig.emul" source "drivers/dma/Kconfig.nxp_edma" +source "drivers/dma/Kconfig.dw_axi_dmac" + endif # DMA diff --git a/drivers/dma/Kconfig.dw_axi_dmac b/drivers/dma/Kconfig.dw_axi_dmac new file mode 100644 index 000000000000..758486a8f0bd --- /dev/null +++ b/drivers/dma/Kconfig.dw_axi_dmac @@ -0,0 +1,53 @@ +# DesignWare DMA configuration options + +# Copyright (c) 2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +config DMA_DW_AXI + bool "DesignWare AXI DMA driver" + default y + depends on DT_HAS_SNPS_DESIGNWARE_DMA_AXI_ENABLED + imply DMA_64BIT + help + DesignWare AXI DMA driver. + +if DMA_DW_AXI + +config DMA_DW_AXI_MAX_DESC + int "allocate number of lli descriptor" + default 10 + help + creates number of descriptor per channel in a statically allocated pool. + Each channel has its own dedicated pool. + +config DMA_DW_AXI_LLI_SUPPORT + bool "hardware supports linked list multi block transfer" + default y + help + This flag can be enabled if hardware support Linked List multi-block transfer + +config DMA_CHANNEL_STATUS_TIMEOUT + int "Channel status timeout" + default 1000 + help + Max timeout to abort or disable the channel + +config DMA_DW_AXI_MAX_BURST_TXN_LEN + int "max burst transaction length" + default 8 + help + set max number of source and destination data units supported + +config DMA_DW_AXI_DATA_WIDTH + int "data bus width" + default 64 + help + update this flag to change the axi master interface data width + +config DMA_DW_AXI_MAX_BLOCK_TS + int "max block size" + default 32767 + help + update this config to set maximum value of block size + +endif # DMA_DW_AXI diff --git a/drivers/dma/dma_dw_axi.c b/drivers/dma/dma_dw_axi.c new file mode 100644 index 000000000000..917f7008876a --- /dev/null +++ b/drivers/dma/dma_dw_axi.c @@ -0,0 +1,912 @@ +/* + * Copyright (c) 2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT snps_designware_dma_axi + +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(dma_designware_axi, CONFIG_DMA_LOG_LEVEL); + +#define DEV_CFG(_dev) ((const struct dma_dw_axi_dev_cfg *)(_dev)->config) +#define DEV_DATA(_dev) ((struct dma_dw_axi_dev_data *const)(_dev)->data) + +/* mask for block transfer size */ +#define BLOCK_TS_MASK GENMASK(21, 0) + +/* blen : number of data units + * blen will always be in power of two + * + * when blen is 1 then set msize to zero otherwise find most significant bit set + * and subtract two (as IP doesn't support number of data items 2) + */ +#define DMA_DW_AXI_GET_MSIZE(blen) ((blen == 1) ? (0U) : (find_msb_set(blen) - 2U)) + +/* Common_Registers_Address_Block */ +#define DMA_DW_AXI_IDREG 0x0 +#define DMA_DW_AXI_COMPVERREG 0x08 +#define DMA_DW_AXI_CFGREG 0x10 +#define DMA_DW_AXI_CHENREG 0x18 +#define DMA_DW_AXI_INTSTATUSREG 0x30 +#define DMA_DW_AXI_COMMONREG_INTCLEARREG 0x38 +#define DMA_DW_AXI_COMMONREG_INTSTATUS_ENABLEREG 0x40 +#define DMA_DW_AXI_COMMONREG_INTSIGNAL_ENABLEREG 0x48 +#define DMA_DW_AXI_COMMONREG_INTSTATUSREG 0x50 +#define DMA_DW_AXI_RESETREG 0x58 +#define DMA_DW_AXI_LOWPOWER_CFGREG 0x60 + +/* Channel enable by setting ch_en and ch_en_we */ +#define CH_EN(chan) (BIT64(8 + chan) | BIT64(chan)) +/* Channel enable by setting ch_susp and ch_susp_we */ +#define CH_SUSP(chan) (BIT64(24 + chan) | BIT64(16 + chan)) +/* Channel enable by setting ch_abort and ch_abort_we */ +#define CH_ABORT(chan) (BIT64(40 + chan) | BIT64(32 + chan)) + +/* channel susp/resume write enable pos */ +#define CH_RESUME_WE(chan) (BIT64(24 + chan)) +/* channel resume bit pos */ +#define CH_RESUME(chan) (BIT64(16 + chan)) + +#define DMA_DW_AXI_CHAN_OFFSET(chan) (0x100 * chan) + +/* source address register for a channel */ +#define DMA_DW_AXI_CH_SAR(chan) (0x100 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* destination address register for a channel */ +#define DMA_DW_AXI_CH_DAR(chan) (0x108 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* block transfer size register for a channel */ +#define DMA_DW_AXI_CH_BLOCK_TS(chan) (0x110 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* channel control register */ +#define DMA_DW_AXI_CH_CTL(chan) (0x118 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* channel configuration register */ +#define DMA_DW_AXI_CH_CFG(chan) (0x120 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* linked list pointer register */ +#define DMA_DW_AXI_CH_LLP(chan) (0x128 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* channel status register */ +#define DMA_DW_AXI_CH_STATUSREG(chan) (0x130 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* channel software handshake source register */ +#define DMA_DW_AXI_CH_SWHSSRCREG(chan) (0x138 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* channel software handshake destination register */ +#define DMA_DW_AXI_CH_SWHSDSTREG(chan) (0x140 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* channel block transfer resume request register */ +#define DMA_DW_AXI_CH_BLK_TFR_RESUMEREQREG(chan) (0x148 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* channel AXI ID rester */ +#define DMA_DW_AXI_CH_AXI_IDREG(chan) (0x150 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* channel AXI QOS register */ +#define DMA_DW_AXI_CH_AXI_QOSREG(chan) (0x158 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* channel interrupt status enable register */ +#define DMA_DW_AXI_CH_INTSTATUS_ENABLEREG(chan) (0x180 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* channel interrupt status register */ +#define DMA_DW_AXI_CH_INTSTATUS(chan) (0x188 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* channel interrupt signal enable register */ +#define DMA_DW_AXI_CH_INTSIGNAL_ENABLEREG(chan) (0x190 + DMA_DW_AXI_CHAN_OFFSET(chan)) +/* channel interrupt clear register */ +#define DMA_DW_AXI_CH_INTCLEARREG(chan) (0x198 + DMA_DW_AXI_CHAN_OFFSET(chan)) + +/* bitfield configuration for multi-block transfer */ +#define DMA_DW_AXI_CFG_SRC_MULTBLK_TYPE(x) FIELD_PREP(GENMASK64(1, 0), x) +#define DMA_DW_AXI_CFG_DST_MULTBLK_TYPE(x) FIELD_PREP(GENMASK64(3, 2), x) + +/* bitfield configuration to assign handshaking interface to source and destination */ +#define DMA_DW_AXI_CFG_SRC_PER(x) FIELD_PREP(GENMASK64(9, 4), x) +#define DMA_DW_AXI_CFG_DST_PER(x) FIELD_PREP(GENMASK64(16, 11), x) + +/* bitfield configuration for transfer type and flow controller */ +#define DMA_DW_AXI_CFG_TT_FC(x) FIELD_PREP(GENMASK64(34, 32), x) + +#define DMA_DW_AXI_CFG_HW_HS_SRC_BIT_POS 35 +#define DMA_DW_AXI_CFG_HW_HS_DST_BIT_POS 36 + +#define DMA_DW_AXI_CFG_PRIORITY(x) FIELD_PREP(GENMASK64(51, 47), x) + +/* descriptor valid or not */ +#define DMA_DW_AXI_CTL_LLI_VALID BIT64(63) +/* descriptor is last or not in a link */ +#define DMA_DW_AXI_CTL_LLI_LAST BIT64(62) +/* interrupt on completion of block transfer */ +#define DMA_DW_AXI_CTL_IOC_BLK_TFR BIT64(58) +/* source status enable bit */ +#define DMA_DW_AXI_CTL_SRC_STAT_EN BIT64(56) +/* destination status enable bit */ +#define DMA_DW_AXI_CTL_DST_STAT_EN BIT64(57) +/* source burst length enable */ +#define DMA_DW_AXI_CTL_ARLEN_EN BIT64(38) +/* source burst length(considered when corresponding enable bit is set) */ +#define DMA_DW_AXI_CTL_ARLEN(x) FIELD_PREP(GENMASK64(46, 39), x) +/* destination burst length enable */ +#define DMA_DW_AXI_CTL_AWLEN_EN BIT64(47) +/* destination burst length(considered when corresponding enable bit is set) */ +#define DMA_DW_AXI_CTL_AWLEN(x) FIELD_PREP(GENMASK64(55, 48), x) + +/* source burst transaction length */ +#define DMA_DW_AXI_CTL_SRC_MSIZE(x) FIELD_PREP(GENMASK64(17, 14), x) +/* destination burst transaction length */ +#define DMA_DW_AXI_CTL_DST_MSIZE(x) FIELD_PREP(GENMASK64(21, 18), x) +/* source transfer width */ +#define DMA_DW_AXI_CTL_SRC_WIDTH(x) FIELD_PREP(GENMASK64(10, 8), x) +/* destination transfer width */ +#define DMA_DW_AXI_CTL_DST_WIDTH(x) FIELD_PREP(GENMASK64(13, 11), x) + +/* mask all the interrupts */ +#define DMA_DW_AXI_IRQ_NONE 0 +/* enable block completion transfer interrupt */ +#define DMA_DW_AXI_IRQ_BLOCK_TFR BIT64(0) +/* enable transfer completion interrupt */ +#define DMA_DW_AXI_IRQ_DMA_TFR BIT64(1) +/* enable interrupts on any dma transfer error */ +#define DMA_DW_AXI_IRQ_ALL_ERR (GENMASK64(14, 5) | GENMASK64(21, 16)) + +/* global enable bit for dma controller */ +#define DMA_DW_AXI_CFG_EN BIT64(0) +/* global enable bit for interrupt */ +#define DMA_DW_AXI_CFG_INT_EN BIT64(1) + +/* descriptor used by dw axi dma controller*/ +struct dma_lli { + uint64_t sar; + uint64_t dar; + uint32_t block_ts_lo; + uint32_t reserved; + uint64_t llp; + uint64_t ctl; + uint32_t sstat; + uint32_t dstat; + uint64_t llp_status; + uint64_t reserved1; +} __aligned(64); + +/* status of the channel */ +enum dma_dw_axi_ch_state { + DMA_DW_AXI_CH_IDLE, + DMA_DW_AXI_CH_SUSPENDED, + DMA_DW_AXI_CH_ACTIVE, + DMA_DW_AXI_CH_PREPARED, +}; + +/* source and destination transfer width */ +enum dma_dw_axi_ch_width { + BITS_8, + BITS_16, + BITS_32, + BITS_64, + BITS_128, + BITS_256, + BITS_512, +}; + +/* transfer direction and flow controller */ +enum dma_dw_axi_tt_fc { + M2M_DMAC, + M2P_DMAC, + P2M_DMAC, + P2P_DMAC, + P2M_SRC, + P2P_SRC, + M2P_DST, + P2P_DST, +}; + +/* type of multi-block transfer */ +enum dma_dw_axi_multi_blk_type { + MULTI_BLK_CONTIGUOUS, + MULTI_BLK_RELOAD, + MULTI_BLK_SHADOW_REG, + MULTI_BLK_LLI, +}; + +/* dma driver channel specific information */ +struct dma_dw_axi_ch_data { + /* lli descriptor base */ + struct dma_lli *lli_desc_base; + /* lli current descriptor */ + struct dma_lli *lli_desc_current; + /* dma channel state */ + enum dma_dw_axi_ch_state ch_state; + /* direction of transfer */ + uint32_t direction; + /* number of descriptors */ + uint32_t lli_desc_count; + /* cfg register configuration for dma transfer */ + uint64_t cfg; + /* mask and unmask interrupts */ + uint64_t irq_unmask; + /* user call back for dma transfer completion */ + dma_callback_t dma_xfer_callback; + /* user data for dma callback for dma transfer completion */ + void *priv_data_xfer; + /* user call back for dma block transfer completion */ + dma_callback_t dma_blk_xfer_callback; + /* user data for dma callback for dma block transfer completion */ + void *priv_data_blk_tfr; +}; + +/* dma controller driver data structure */ +struct dma_dw_axi_dev_data { + /* dma context */ + struct dma_context dma_ctx; + + /* mmio address mapping info for dma controller */ + DEVICE_MMIO_NAMED_RAM(dma_mmio); + /* pointer to store channel specific info */ + struct dma_dw_axi_ch_data *chan; + /* pointer to hold descriptor base address */ + struct dma_lli *dma_desc_pool; +}; + +/* Device constant configuration parameters */ +struct dma_dw_axi_dev_cfg { + /* dma address space to map */ + DEVICE_MMIO_NAMED_ROM(dma_mmio); + +#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(resets) + /* Reset controller device configurations */ + const struct reset_dt_spec reset; +#endif + /* dma controller interrupt configuration function pointer */ + void (*irq_config)(void); +}; + +/** + * @brief get current status of the channel + * + * @param dev Pointer to the device structure for the driver instance + * @param channel channel number + * + * @retval status of the channel + */ +static enum dma_dw_axi_ch_state dma_dw_axi_get_ch_status(const struct device *dev, uint32_t ch) +{ + uint32_t bit_status; + uint64_t ch_status; + uintptr_t reg_base = DEVICE_MMIO_NAMED_GET(dev, dma_mmio); + + ch_status = sys_read64(reg_base + DMA_DW_AXI_CHENREG); + + /* channel is active/busy in the dma transfer */ + bit_status = ((ch_status >> ch) & 1); + if (bit_status) { + return DMA_DW_AXI_CH_ACTIVE; + } + + /* channel is currently suspended */ + bit_status = ((ch_status >> (16 + ch)) & 1); + if (bit_status) { + return DMA_DW_AXI_CH_SUSPENDED; + } + + /* channel is idle */ + return DMA_DW_AXI_CH_IDLE; +} + +static void dma_dw_axi_isr(const struct device *dev) +{ + unsigned int channel; + uint64_t status, ch_status; + int ret_status = 0; + struct dma_dw_axi_ch_data *chan_data; + uintptr_t reg_base = DEVICE_MMIO_NAMED_GET(dev, dma_mmio); + struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); + + /* read interrupt status register to find interrupt is for which channel */ + status = sys_read64(reg_base + DMA_DW_AXI_INTSTATUSREG); + channel = find_lsb_set(status) - 1; + if (channel < 0) { + LOG_ERR("Spurious interrupt received channel:%u\n", channel); + return; + } + + if (channel > (dw_dev_data->dma_ctx.dma_channels - 1)) { + LOG_ERR("Interrupt received on invalid channel:%d\n", channel); + return; + } + + /* retrieve channel specific data pointer for a channel */ + chan_data = &dw_dev_data->chan[channel]; + + /* get dma transfer status */ + ch_status = sys_read64(reg_base + DMA_DW_AXI_CH_INTSTATUS(channel)); + if (!ch_status) { + LOG_ERR("Spurious interrupt received ch_status:0x%llx\n", ch_status); + return; + } + + /* handle dma transfer errors if any */ + if (ch_status & DMA_DW_AXI_IRQ_ALL_ERR) { + sys_write64(DMA_DW_AXI_IRQ_ALL_ERR, + reg_base + DMA_DW_AXI_CH_INTCLEARREG(channel)); + LOG_ERR("DMA Error: Channel:%d Channel interrupt status:0x%llx\n", + channel, ch_status); + ret_status = -(ch_status & DMA_DW_AXI_IRQ_ALL_ERR); + } + + /* handle block transfer completion */ + if (ch_status & DMA_DW_AXI_IRQ_BLOCK_TFR) { + sys_write64(DMA_DW_AXI_IRQ_ALL_ERR | DMA_DW_AXI_IRQ_BLOCK_TFR, + reg_base + DMA_DW_AXI_CH_INTCLEARREG(channel)); + + if (chan_data->dma_blk_xfer_callback) { + chan_data->dma_blk_xfer_callback(dev, + chan_data->priv_data_blk_tfr, channel, ret_status); + } + } + + /* handle dma transfer completion */ + if (ch_status & DMA_DW_AXI_IRQ_DMA_TFR) { + sys_write64(DMA_DW_AXI_IRQ_ALL_ERR | DMA_DW_AXI_IRQ_DMA_TFR, + reg_base + DMA_DW_AXI_CH_INTCLEARREG(channel)); + + if (chan_data->dma_xfer_callback) { + chan_data->dma_xfer_callback(dev, chan_data->priv_data_xfer, + channel, ret_status); + chan_data->ch_state = dma_dw_axi_get_ch_status(dev, channel); + } + } +} + +/** + * @brief set data source and destination data width + * + * @param lli_desc Pointer to the descriptor + * @param src_data_width source data width + * @param dest_data_width destination data width + * + * @retval 0 on success, -ENOTSUP if the data width is not supported + */ +static int dma_dw_axi_set_data_width(struct dma_lli *lli_desc, + uint32_t src_data_width, uint32_t dest_data_width) +{ + if (src_data_width > CONFIG_DMA_DW_AXI_DATA_WIDTH || + dest_data_width > CONFIG_DMA_DW_AXI_DATA_WIDTH) { + LOG_ERR("transfer width more than %u not supported", CONFIG_DMA_DW_AXI_DATA_WIDTH); + return -ENOTSUP; + } + + switch (src_data_width) { + case 1: + /* one byte transfer */ + lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_8); + break; + case 2: + /* 2-bytes transfer width */ + lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_16); + break; + case 4: + /* 4-bytes transfer width */ + lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_32); + break; + case 8: + /* 8-bytes transfer width */ + lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_64); + break; + case 16: + /* 16-bytes transfer width */ + lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_128); + break; + case 32: + /* 32-bytes transfer width */ + lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_256); + break; + case 64: + /* 64-bytes transfer width */ + lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_WIDTH(BITS_512); + break; + default: + LOG_ERR("Source transfer width not supported"); + return -ENOTSUP; + } + + switch (dest_data_width) { + case 1: + /* one byte transfer */ + lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_8); + break; + case 2: + /* 2-bytes transfer width */ + lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_16); + break; + case 4: + /* 4-bytes transfer width */ + lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_32); + break; + case 8: + /* 8-bytes transfer width */ + lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_64); + break; + case 16: + /* 16-bytes transfer width */ + lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_128); + break; + case 32: + /* 32-bytes transfer width */ + lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_256); + break; + case 64: + /* 64-bytes transfer width */ + lli_desc->ctl |= DMA_DW_AXI_CTL_DST_WIDTH(BITS_512); + break; + default: + LOG_ERR("Destination transfer width not supported"); + return -ENOTSUP; + } + + return 0; +} + +static int dma_dw_axi_config(const struct device *dev, uint32_t channel, + struct dma_config *cfg) +{ + int ret; + uint32_t msize_src, msize_dst, i, ch_state; + struct dma_dw_axi_ch_data *chan_data; + struct dma_block_config *blk_cfg; + struct dma_lli *lli_desc; + struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); + + /* check for invalid parameters before dereferencing them. */ + if (cfg == NULL) { + LOG_ERR("invalid dma config :%p", cfg); + return -ENODATA; + } + + /* check if the channel is valid */ + if (channel > (dw_dev_data->dma_ctx.dma_channels - 1)) { + LOG_ERR("invalid dma channel %d", channel); + return -EINVAL; + } + + /* return if the channel is not idle */ + ch_state = dma_dw_axi_get_ch_status(dev, channel); + if (ch_state != DMA_DW_AXI_CH_IDLE) { + LOG_ERR("DMA channel:%d is not idle(status:%d)", channel, ch_state); + return -EBUSY; + } + + if (!cfg->block_count) { + LOG_ERR("no blocks to transfer"); + return -EINVAL; + } + + /* descriptor should be less than max configured descriptor */ + if (cfg->block_count > CONFIG_DMA_DW_AXI_MAX_DESC) { + LOG_ERR("dma:%s channel %d descriptor block count: %d larger than" + " max descriptors in pool: %d", dev->name, channel, + cfg->block_count, CONFIG_DMA_DW_AXI_MAX_DESC); + return -EINVAL; + } + + if (cfg->source_burst_length > CONFIG_DMA_DW_AXI_MAX_BURST_TXN_LEN || + cfg->dest_burst_length > CONFIG_DMA_DW_AXI_MAX_BURST_TXN_LEN || + cfg->source_burst_length == 0 || cfg->dest_burst_length == 0) { + LOG_ERR("dma:%s burst length not supported", dev->name); + return -ENOTSUP; + } + + /* get channel specific data pointer */ + chan_data = &dw_dev_data->chan[channel]; + + /* check if the channel is currently idle */ + if (chan_data->ch_state != DMA_DW_AXI_CH_IDLE) { + LOG_ERR("DMA channel:%d is busy", channel); + return -EBUSY; + } + + /* burst transaction length for source and destination */ + msize_src = DMA_DW_AXI_GET_MSIZE(cfg->source_burst_length); + msize_dst = DMA_DW_AXI_GET_MSIZE(cfg->dest_burst_length); + + chan_data->cfg = 0; + chan_data->irq_unmask = 0; + + chan_data->direction = cfg->channel_direction; + + chan_data->lli_desc_base = + &dw_dev_data->dma_desc_pool[channel * CONFIG_DMA_DW_AXI_MAX_DESC]; + chan_data->lli_desc_count = cfg->block_count; + memset(chan_data->lli_desc_base, 0, + sizeof(struct dma_lli) * chan_data->lli_desc_count); + + lli_desc = chan_data->lli_desc_base; + blk_cfg = cfg->head_block; + + /* max channel priority can be MAX_CHANNEL - 1 */ + if (cfg->channel_priority < dw_dev_data->dma_ctx.dma_channels) { + chan_data->cfg |= DMA_DW_AXI_CFG_PRIORITY(cfg->channel_priority); + } + + /* configure all the descriptors in a loop */ + for (i = 0; i < cfg->block_count; i++) { + + ret = dma_dw_axi_set_data_width(lli_desc, cfg->source_data_size, + cfg->dest_data_size); + if (ret) { + return ret; + } + + lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_STAT_EN | + DMA_DW_AXI_CTL_DST_STAT_EN | DMA_DW_AXI_CTL_IOC_BLK_TFR; + + lli_desc->sar = blk_cfg->source_address; + lli_desc->dar = blk_cfg->dest_address; + + /* set block transfer size*/ + lli_desc->block_ts_lo = (blk_cfg->block_size / cfg->source_data_size) - 1; + if (lli_desc->block_ts_lo > CONFIG_DMA_DW_AXI_MAX_BLOCK_TS) { + LOG_ERR("block transfer size more than %u not supported", + CONFIG_DMA_DW_AXI_MAX_BLOCK_TS); + return -ENOTSUP; + } + + /* configuration based on channel direction */ + if (cfg->channel_direction == MEMORY_TO_MEMORY) { + chan_data->cfg |= DMA_DW_AXI_CFG_TT_FC(M2M_DMAC); + + lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_MSIZE(msize_src) | + DMA_DW_AXI_CTL_DST_MSIZE(msize_dst); + + } else if (cfg->channel_direction == MEMORY_TO_PERIPHERAL) { + + chan_data->cfg |= DMA_DW_AXI_CFG_TT_FC(M2P_DMAC); + lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_MSIZE(msize_src) | + DMA_DW_AXI_CTL_DST_MSIZE(msize_dst); + WRITE_BIT(chan_data->cfg, DMA_DW_AXI_CFG_HW_HS_DST_BIT_POS, 0); + + /* assign a hardware handshake interface */ + chan_data->cfg |= DMA_DW_AXI_CFG_DST_PER(cfg->dma_slot); + + } else if (cfg->channel_direction == PERIPHERAL_TO_MEMORY) { + lli_desc->ctl |= DMA_DW_AXI_CTL_SRC_MSIZE(msize_src) | + DMA_DW_AXI_CTL_DST_MSIZE(msize_dst); + chan_data->cfg |= DMA_DW_AXI_CFG_TT_FC(P2M_DMAC); + WRITE_BIT(chan_data->cfg, DMA_DW_AXI_CFG_HW_HS_SRC_BIT_POS, 0); + + /* assign a hardware handshake interface */ + chan_data->cfg |= DMA_DW_AXI_CFG_SRC_PER(cfg->dma_slot); + + } else { + LOG_ERR("%s: dma %s channel %d invalid direction %d", + __func__, dev->name, channel, cfg->channel_direction); + + return -EINVAL; + } + + /* set pointer to the next descriptor */ + lli_desc->llp = ((uint64_t)(lli_desc + 1)); + +#if defined(CONFIG_DMA_DW_AXI_LLI_SUPPORT) + /* configure multi block transfer size as linked list */ + chan_data->cfg |= DMA_DW_AXI_CFG_SRC_MULTBLK_TYPE(MULTI_BLK_LLI) | + DMA_DW_AXI_CFG_DST_MULTBLK_TYPE(MULTI_BLK_LLI); + + lli_desc->ctl |= DMA_DW_AXI_CTL_LLI_VALID; + /* last descriptor*/ + if ((i + 1) == chan_data->lli_desc_count) { + lli_desc->ctl |= DMA_DW_AXI_CTL_LLI_LAST | DMA_DW_AXI_CTL_LLI_VALID; + lli_desc->llp = 0; + } +#else + /* configure multi-block transfer as contiguous mode */ + chan_data->cfg |= DMA_DW_AXI_CFG_SRC_MULTBLK_TYPE(MULTI_BLK_CONTIGUOUS) | + DMA_DW_AXI_CFG_DST_MULTBLK_TYPE(MULTI_BLK_CONTIGUOUS); +#endif + + /* next descriptor to configure*/ + lli_desc++; + blk_cfg = blk_cfg->next_block; + } + + arch_dcache_flush_range((void *)chan_data->lli_desc_base, + sizeof(struct dma_lli) * cfg->block_count); + + chan_data->lli_desc_current = chan_data->lli_desc_base; + + /* enable an interrupt depending on whether the callback is requested after dma transfer + * completion or dma block transfer completion + * + * disable an interrupt if callback is not requested + */ + if (cfg->dma_callback && cfg->complete_callback_en) { + chan_data->dma_blk_xfer_callback = cfg->dma_callback; + chan_data->priv_data_blk_tfr = cfg->user_data; + + chan_data->irq_unmask = DMA_DW_AXI_IRQ_BLOCK_TFR | DMA_DW_AXI_IRQ_DMA_TFR; + } else if (cfg->dma_callback && !cfg->complete_callback_en) { + chan_data->dma_xfer_callback = cfg->dma_callback; + chan_data->priv_data_xfer = cfg->user_data; + + chan_data->irq_unmask = DMA_DW_AXI_IRQ_DMA_TFR; + } else { + chan_data->irq_unmask = DMA_DW_AXI_IRQ_NONE; + } + + /* unmask error interrupts when error_callback_dis is 0 */ + if (!cfg->error_callback_dis) { + chan_data->irq_unmask |= DMA_DW_AXI_IRQ_ALL_ERR; + } + + /* dma descriptors are configured, ready to start dma transfer */ + chan_data->ch_state = DMA_DW_AXI_CH_PREPARED; + + return 0; +} + +static int dma_dw_axi_start(const struct device *dev, uint32_t channel) +{ + uint32_t ch_state; + struct dma_dw_axi_ch_data *chan_data; + struct dma_lli *lli_desc; + struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); + uintptr_t reg_base = DEVICE_MMIO_NAMED_GET(dev, dma_mmio); + + /* validate channel number */ + if (channel > (dw_dev_data->dma_ctx.dma_channels - 1)) { + LOG_ERR("invalid dma channel %d", channel); + return -EINVAL; + } + + /* check whether channel is idle before initiating DMA transfer */ + ch_state = dma_dw_axi_get_ch_status(dev, channel); + if (ch_state != DMA_DW_AXI_CH_IDLE) { + LOG_ERR("DMA channel:%d is not idle", channel); + return -EBUSY; + } + + /* get channel specific data pointer */ + chan_data = &dw_dev_data->chan[channel]; + + if (chan_data->ch_state != DMA_DW_AXI_CH_PREPARED) { + LOG_ERR("DMA descriptors not configured"); + return -EINVAL; + } + + /* enable dma controller and global interrupt bit */ + sys_write64(DMA_DW_AXI_CFG_INT_EN | DMA_DW_AXI_CFG_EN, reg_base + DMA_DW_AXI_CFGREG); + + sys_write64(chan_data->cfg, reg_base + DMA_DW_AXI_CH_CFG(channel)); + + sys_write64(chan_data->irq_unmask, + reg_base + DMA_DW_AXI_CH_INTSTATUS_ENABLEREG(channel)); + sys_write64(chan_data->irq_unmask, + reg_base + DMA_DW_AXI_CH_INTSIGNAL_ENABLEREG(channel)); + + lli_desc = chan_data->lli_desc_current; + +#if defined(CONFIG_DMA_DW_AXI_LLI_SUPPORT) + sys_write64(((uint64_t)lli_desc), reg_base + DMA_DW_AXI_CH_LLP(channel)); +#else + /* Program Source and Destination addresses */ + sys_write64(lli_desc->sar, reg_base + DMA_DW_AXI_CH_SAR(channel)); + sys_write64(lli_desc->dar, reg_base + DMA_DW_AXI_CH_DAR(channel)); + + sys_write64(lli_desc->block_ts_lo & BLOCK_TS_MASK, + reg_base + DMA_DW_AXI_CH_BLOCK_TS(channel)); + + /* Program CH.CTL register */ + sys_write64(lli_desc->ctl, reg_base + DMA_DW_AXI_CH_CTL(channel)); +#endif + + /* Enable the channel which will initiate DMA transfer */ + sys_write64(CH_EN(channel), reg_base + DMA_DW_AXI_CHENREG); + + chan_data->ch_state = dma_dw_axi_get_ch_status(dev, channel); + + return 0; +} + +static int dma_dw_axi_stop(const struct device *dev, uint32_t channel) +{ + bool is_channel_busy; + uint32_t ch_state; + struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); + uintptr_t reg_base = DEVICE_MMIO_NAMED_GET(dev, dma_mmio); + + /* channel should be valid */ + if (channel > (dw_dev_data->dma_ctx.dma_channels - 1)) { + LOG_ERR("invalid dma channel %d", channel); + return -EINVAL; + } + + /* return if the channel is idle as there is nothing to stop */ + ch_state = dma_dw_axi_get_ch_status(dev, channel); + if (ch_state == DMA_DW_AXI_CH_IDLE) { + /* channel is already idle */ + return 0; + } + + /* To stop transfer or abort the channel in case of abnormal state: + * 1. To disable channel, first suspend channel and drain the FIFO + * 2. Disable the channel. Channel may get hung and can't be disabled + * if there is no response from peripheral + * 3. If channel is not disabled, Abort the channel. Aborting channel will + * Flush out FIFO and data will be lost. Then corresponding interrupt will + * be raised for abort and CH_EN bit will be cleared from CHENREG register + */ + sys_write64(CH_SUSP(channel), reg_base + DMA_DW_AXI_CHENREG); + + /* Try to disable the channel */ + sys_clear_bit(reg_base + DMA_DW_AXI_CHENREG, channel); + + is_channel_busy = WAIT_FOR((sys_read64(reg_base + DMA_DW_AXI_CHENREG)) & (BIT(channel)), + CONFIG_DMA_CHANNEL_STATUS_TIMEOUT, k_busy_wait(10)); + if (is_channel_busy) { + LOG_WRN("No response from handshaking interface... Aborting a channel..."); + sys_write64(CH_ABORT(channel), reg_base + DMA_DW_AXI_CHENREG); + + is_channel_busy = WAIT_FOR((sys_read64(reg_base + DMA_DW_AXI_CHENREG)) & + (BIT(channel)), CONFIG_DMA_CHANNEL_STATUS_TIMEOUT, + k_busy_wait(10)); + if (is_channel_busy) { + LOG_ERR("Channel abort failed"); + return -EBUSY; + } + } + + return 0; +} + +static int dma_dw_axi_resume(const struct device *dev, uint32_t channel) +{ + uint32_t reg; + uintptr_t reg_base = DEVICE_MMIO_NAMED_GET(dev, dma_mmio); + struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); + uint32_t ch_state; + + /* channel should be valid */ + if (channel > (dw_dev_data->dma_ctx.dma_channels - 1)) { + LOG_ERR("invalid dma channel %d", channel); + return -EINVAL; + } + + ch_state = dma_dw_axi_get_ch_status(dev, channel); + if (ch_state != DMA_DW_AXI_CH_SUSPENDED) { + LOG_INF("channel %u is not in suspended state so cannot resume channel", channel); + return 0; + } + + reg = sys_read64(reg_base + DMA_DW_AXI_CHENREG); + /* channel susp write enable bit has to be asserted */ + WRITE_BIT(reg, CH_RESUME_WE(channel), 1); + /* channel susp bit must be cleared to resume a channel*/ + WRITE_BIT(reg, CH_RESUME(channel), 0); + /* resume a channel by writing 0: ch_susp and 1: ch_susp_we */ + sys_write64(reg, reg_base + DMA_DW_AXI_CHENREG); + + return 0; +} + +/* suspend a dma channel */ +static int dma_dw_axi_suspend(const struct device *dev, uint32_t channel) +{ + int ret; + uintptr_t reg_base = DEVICE_MMIO_NAMED_GET(dev, dma_mmio); + struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); + uint32_t ch_state; + + /* channel should be valid */ + if (channel > (dw_dev_data->dma_ctx.dma_channels - 1)) { + LOG_ERR("invalid dma channel %u", channel); + return -EINVAL; + } + + ch_state = dma_dw_axi_get_ch_status(dev, channel); + if (ch_state != DMA_DW_AXI_CH_ACTIVE) { + LOG_INF("nothing to suspend as dma channel %u is not busy", channel); + return 0; + } + + /* suspend dma transfer */ + sys_write64(CH_SUSP(channel), reg_base + DMA_DW_AXI_CHENREG); + + ret = WAIT_FOR(dma_dw_axi_get_ch_status(dev, channel) & + DMA_DW_AXI_CH_SUSPENDED, CONFIG_DMA_CHANNEL_STATUS_TIMEOUT, + k_busy_wait(10)); + if (ret == 0) { + LOG_ERR("channel suspend failed"); + return ret; + } + + return 0; +} + +static int dma_dw_axi_init(const struct device *dev) +{ + DEVICE_MMIO_NAMED_MAP(dev, dma_mmio, K_MEM_CACHE_NONE); + int i, ret; + struct dma_dw_axi_ch_data *chan_data; + const struct dma_dw_axi_dev_cfg *dw_dma_config = DEV_CFG(dev); + struct dma_dw_axi_dev_data *const dw_dev_data = DEV_DATA(dev); + +#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(resets) + + if (dw_dma_config->reset.dev != NULL) { + /* check if reset manager is in ready state */ + if (!device_is_ready(dw_dma_config->reset.dev)) { + LOG_ERR("reset controller device not found"); + return -ENODEV; + } + + /* assert and de-assert dma controller */ + ret = reset_line_toggle(dw_dma_config->reset.dev, dw_dma_config->reset.id); + if (ret != 0) { + LOG_ERR("failed to reset dma controller"); + return ret; + } + } +#endif + + /* initialize channel state variable */ + for (i = 0; i < dw_dev_data->dma_ctx.dma_channels; i++) { + chan_data = &dw_dev_data->chan[i]; + /* initialize channel state */ + chan_data->ch_state = DMA_DW_AXI_CH_IDLE; + } + + /* configure and enable interrupt lines */ + dw_dma_config->irq_config(); + + return 0; +} + +static const struct dma_driver_api dma_dw_axi_driver_api = { + .config = dma_dw_axi_config, + .start = dma_dw_axi_start, + .stop = dma_dw_axi_stop, + .suspend = dma_dw_axi_suspend, + .resume = dma_dw_axi_resume, +}; + +/* enable irq lines */ +#define CONFIGURE_DMA_IRQ(idx, inst) \ + IF_ENABLED(DT_INST_IRQ_HAS_IDX(inst, idx), ( \ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(inst, idx, irq), \ + DT_INST_IRQ_BY_IDX(inst, idx, priority), \ + dma_dw_axi_isr, \ + DEVICE_DT_INST_GET(inst), 0); \ + irq_enable(DT_INST_IRQ_BY_IDX(inst, idx, irq)); \ + )) + +#define DW_AXI_DMA_RESET_SPEC_INIT(inst) \ + .reset = RESET_DT_SPEC_INST_GET(inst), \ + +#define DW_AXI_DMAC_INIT(inst) \ + static struct dma_dw_axi_ch_data chan_##inst[DT_INST_PROP(inst, dma_channels)]; \ + static struct dma_lli \ + dma_desc_pool_##inst[DT_INST_PROP(inst, dma_channels) * \ + CONFIG_DMA_DW_AXI_MAX_DESC]; \ + ATOMIC_DEFINE(dma_dw_axi_atomic##inst, \ + DT_INST_PROP(inst, dma_channels)); \ + static struct dma_dw_axi_dev_data dma_dw_axi_data_##inst = { \ + .dma_ctx = { \ + .magic = DMA_MAGIC, \ + .atomic = dma_dw_axi_atomic##inst, \ + .dma_channels = DT_INST_PROP(inst, dma_channels), \ + }, \ + .chan = chan_##inst, \ + .dma_desc_pool = dma_desc_pool_##inst, \ + }; \ + static void dw_dma_irq_config_##inst(void); \ + static const struct dma_dw_axi_dev_cfg dma_dw_axi_config_##inst = { \ + DEVICE_MMIO_NAMED_ROM_INIT(dma_mmio, DT_DRV_INST(inst)), \ + IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, resets), \ + (DW_AXI_DMA_RESET_SPEC_INIT(inst))) \ + .irq_config = dw_dma_irq_config_##inst, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, \ + &dma_dw_axi_init, \ + NULL, \ + &dma_dw_axi_data_##inst, \ + &dma_dw_axi_config_##inst, POST_KERNEL, \ + CONFIG_DMA_INIT_PRIORITY, \ + &dma_dw_axi_driver_api); \ + \ + static void dw_dma_irq_config_##inst(void) \ + { \ + LISTIFY(DT_NUM_IRQS(DT_DRV_INST(inst)), CONFIGURE_DMA_IRQ, (), inst) \ + } + +DT_INST_FOREACH_STATUS_OKAY(DW_AXI_DMAC_INIT) diff --git a/dts/bindings/dma/snps,designware-dma-axi.yaml b/dts/bindings/dma/snps,designware-dma-axi.yaml new file mode 100644 index 000000000000..baf27c45787d --- /dev/null +++ b/dts/bindings/dma/snps,designware-dma-axi.yaml @@ -0,0 +1,36 @@ +# Copyright (c) 2023 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +description: Synopsys Designware axi DMA Controller node + +compatible: "snps,designware-dma-axi" + +include: [dma-controller.yaml, reset-device.yaml] + +properties: + reg: + required: true + + dma-channels: + required: true + + interrupts: + required: true + + "#dma-cells": + const: 1 + +# #dma-cells : Must be <1>. +# The 1st cell specifies the hardware handshaking signal ID +# Example of device-tree dma channel configuration: +# +# &spi0 { +# /* Configure DMA */ +# dmas = <&dma0 18>, <&dma0 19>; +# dma-names = "tx", "rx"; +# }; +# +# In above spi node numbers 18 and 19 represents the +# peripheral handshaking interface ID +dma-cells: + - slot From d3af601803b0f387aafa641a5232b956f9d39a4f Mon Sep 17 00:00:00 2001 From: Balsundar Ponnusamy Date: Sat, 12 Aug 2023 08:29:41 +0000 Subject: [PATCH 2/4] dts: arm64: intel: add dts node for dma controller for agilex5 add dts support for dma to accomodate dma driver bringup on agilex5 Signed-off-by: Balsundar Ponnusamy --- dts/arm64/intel/intel_socfpga_agilex5.dtsi | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/dts/arm64/intel/intel_socfpga_agilex5.dtsi b/dts/arm64/intel/intel_socfpga_agilex5.dtsi index b23830521bfe..4d0d601dc8d7 100644 --- a/dts/arm64/intel/intel_socfpga_agilex5.dtsi +++ b/dts/arm64/intel/intel_socfpga_agilex5.dtsi @@ -239,4 +239,39 @@ status = "disabled"; }; + dma0: dma@10DB0000 { + compatible = "snps,designware-dma-axi"; + #dma-cells = <1>; + reg = <0x10DB0000 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + dma-channels = <4>; + resets = <&reset RSTMGR_DMA_RSTLINE>; + status = "disabled"; + }; + + dma1: dma@10DC0000 { + compatible = "snps,designware-dma-axi"; + #dma-cells = <1>; + reg = <0x10DC0000 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + dma-channels = <4>; + resets = <&reset RSTMGR_DMA_RSTLINE>; + status = "disabled"; + }; }; From c37561445499db34125e0f44e677b1c2e337b224 Mon Sep 17 00:00:00 2001 From: Balsundar Ponnusamy Date: Fri, 10 Feb 2023 07:00:05 +0000 Subject: [PATCH 3/4] CODEOWNERS: updated codeowner for DMA AXI driver updated codeowners file Signed-off-by: Balsundar Ponnusamy --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index f669fcd91a01..9a193897447a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -188,6 +188,7 @@ /drivers/dai/intel/ssp/ @kv2019i @marcinszkudlinski @abonislawski /drivers/dai/intel/dmic/ @marcinszkudlinski @abonislawski /drivers/dai/intel/alh/ @abonislawski +/drivers/dma/dma_dw_axi.c @pbalsundar /drivers/dma/*dw* @tbursztyka /drivers/dma/*dw_common* @abonislawski /drivers/dma/*sam0* @Sizurka From 00c28828ee1c2842ff9c681b262b20f156b6a21f Mon Sep 17 00:00:00 2001 From: Balsundar Ponnusamy Date: Mon, 1 Apr 2024 17:29:48 +0000 Subject: [PATCH 4/4] tests: drivers: dma: scatter_gather: adding agilex5 board support added overlay and conf file for agilex5 board Signed-off-by: Balsundar Ponnusamy --- .../boards/intel_socfpga_agilex5_socdk.conf | 1 + .../boards/intel_socfpga_agilex5_socdk.overlay | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/drivers/dma/scatter_gather/boards/intel_socfpga_agilex5_socdk.conf create mode 100644 tests/drivers/dma/scatter_gather/boards/intel_socfpga_agilex5_socdk.overlay diff --git a/tests/drivers/dma/scatter_gather/boards/intel_socfpga_agilex5_socdk.conf b/tests/drivers/dma/scatter_gather/boards/intel_socfpga_agilex5_socdk.conf new file mode 100644 index 000000000000..c448e14811f7 --- /dev/null +++ b/tests/drivers/dma/scatter_gather/boards/intel_socfpga_agilex5_socdk.conf @@ -0,0 +1 @@ +CONFIG_NOCACHE_MEMORY=y diff --git a/tests/drivers/dma/scatter_gather/boards/intel_socfpga_agilex5_socdk.overlay b/tests/drivers/dma/scatter_gather/boards/intel_socfpga_agilex5_socdk.overlay new file mode 100644 index 000000000000..3feef4ce76a9 --- /dev/null +++ b/tests/drivers/dma/scatter_gather/boards/intel_socfpga_agilex5_socdk.overlay @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + dma0 = &dma0; + }; +}; + +&dma0 { + status = "okay"; +};