You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2022/10/03 19:01:37 UTC

[incubator-nuttx] branch master updated: bl602: Add initial DMA support, including SPI over DMA.

This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 102c63be9e bl602: Add initial DMA support, including SPI over DMA.
102c63be9e is described below

commit 102c63be9ea325a0f4be2288b0aef77eaeace862
Author: Brennan Ashton <ba...@brennanashton.com>
AuthorDate: Sun Aug 14 16:29:30 2022 -0700

    bl602: Add initial DMA support, including SPI over DMA.
---
 arch/risc-v/src/bl602/Kconfig                      |  19 +
 arch/risc-v/src/bl602/Make.defs                    |   4 +
 arch/risc-v/src/bl602/bl602_dma.c                  | 491 +++++++++++++++++++++
 arch/risc-v/src/bl602/bl602_dma.h                  | 262 +++++++++++
 arch/risc-v/src/bl602/bl602_romapi.h               |   3 +
 arch/risc-v/src/bl602/bl602_spi.c                  | 426 +++++++++++++++++-
 arch/risc-v/src/bl602/bl602_spi.h                  |   4 -
 arch/risc-v/src/bl602/hardware/bl602_dma.h         |  31 ++
 arch/risc-v/src/common/riscv_initialize.c          |  13 +
 arch/risc-v/src/common/riscv_internal.h            |   6 +
 boards/risc-v/bl602/bl602evb/configs/dma/defconfig |  82 ++++
 11 files changed, 1327 insertions(+), 14 deletions(-)

diff --git a/arch/risc-v/src/bl602/Kconfig b/arch/risc-v/src/bl602/Kconfig
index 03de245b00..728fe9e1c3 100644
--- a/arch/risc-v/src/bl602/Kconfig
+++ b/arch/risc-v/src/bl602/Kconfig
@@ -7,6 +7,11 @@ comment "BL602 Configuration Options"
 
 menu "BL602 Peripheral Support"
 
+config BL602_DMA
+	bool "DMA"
+	default n
+	select ARCH_DMA
+
 config BL602_HAVE_UART0
 	bool "UART0"
 	select BL602_UART0
@@ -42,9 +47,23 @@ config BL602_PWM0
 config BL602_I2C0
 	bool "I2C0"
 
+config BL602_I2C_DMA
+	bool "I2C DMA support"
+	default n
+	depends on BL602_DMA
+	---help---
+		Select to enable DMA SPI transfers
+
 config BL602_SPI0
 	bool "SPI0"
 
+config BL602_SPI_DMA
+	bool "SPI DMA support"
+	default n
+	depends on BL602_DMA
+	---help---
+		Select to enable DMA SPI transfers
+
 config BL602_RTC
 	bool "RTC"
 
diff --git a/arch/risc-v/src/bl602/Make.defs b/arch/risc-v/src/bl602/Make.defs
index 6aebdda9e3..4f0c44b944 100644
--- a/arch/risc-v/src/bl602/Make.defs
+++ b/arch/risc-v/src/bl602/Make.defs
@@ -58,6 +58,10 @@ ifeq ($(CONFIG_RTC_DRIVER),y)
 CHIP_CSRCS += bl602_rtc.c bl602_rtc_lowerhalf.c
 endif
 
+ifeq ($(CONFIG_BL602_DMA),y)
+CHIP_CSRCS += bl602_dma.c
+endif
+
 CHIP_CSRCS += bl602_glb.c bl602_gpio.c bl602_hbn.c bl602_systemreset.c
 
 ifeq ($(CONFIG_BL602_WIRELESS),y)
diff --git a/arch/risc-v/src/bl602/bl602_dma.c b/arch/risc-v/src/bl602/bl602_dma.c
new file mode 100644
index 0000000000..b51bed091f
--- /dev/null
+++ b/arch/risc-v/src/bl602/bl602_dma.c
@@ -0,0 +1,491 @@
+/****************************************************************************
+ * arch/risc-v/src/bl602/bl602_dma.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/arch.h>
+
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+#include "hardware/bl602_dma.h"
+#include "bl602_dma.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure describes one DMA channel */
+
+struct dma_channel_s
+{
+  uint8_t              chan;          /* DMA channel number (0-BL602_DMA_NCHANNELS) */
+  bool                 inuse;         /* TRUE: The DMA channel is in use */
+  bl602_dma_callback_t callback;      /* Callback invoked when the DMA completes */
+  void                 *arg;          /* Argument passed to callback function */
+};
+
+/* This structure describes the state of the DMA controller */
+
+struct dma_controller_s
+{
+  sem_t exclsem; /* Protects channel table */
+  sem_t chansem; /* Count of free channels */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is the overall state of the DMA controller */
+
+static struct dma_controller_s g_dmac;
+
+/* This is the array of all DMA channels */
+
+static struct dma_channel_s g_dmach[BL602_DMA_NCHANNELS];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bl602_dma_int_handler
+ *
+ * Description:
+ *   DMA interrupt handler.
+ *
+ ****************************************************************************/
+
+static int bl602_dma_int_handler(int irq, void *context, void *arg)
+{
+  /* We need to ack the IRQ or a mess is made */
+
+  /* Itterate over each of the channels checking for and clearing:
+   * DMA_INTTCSTATUS
+   * DMA_INTERRORSTATUS
+   */
+
+  uint8_t ch;
+  uint32_t tc_status;
+  uint32_t err_status;
+
+  tc_status = getreg32(BL602_DMA_INTTCSTATUS) & DMA_INTTCSTATUS_MASK;
+  err_status = getreg32(BL602_DMA_INTERRORSTATUS) & DMA_INTERRORSTATUS_MASK;
+
+  for (ch = 0; ch < BL602_DMA_NCHANNELS; ch++)
+    {
+      if (tc_status & (1 << ch))
+        {
+          dmainfo("CH %d TC Int fired\n", ch);
+          putreg32((1 << ch), BL602_DMA_INTTCCLEAR);
+          if (g_dmach[ch].callback != NULL)
+            {
+              g_dmach[ch].callback(
+                ch,
+                BL602_DMA_INT_EVT_TC,
+                g_dmach[ch].arg);
+            }
+        }
+
+      if (err_status & (1 << ch))
+        {
+          dmainfo("CH %d Error Int fired\n", ch);
+          putreg32((1 << ch), BL602_DMA_INTERRCLR);
+          g_dmach[ch].callback(ch, BL602_DMA_INT_EVT_ERR, g_dmach[ch].arg);
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bl602_dma_channel_request
+ *
+ * Description:
+ *   Allocate a new DMA channel.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   0-3: DMA channel
+ *   -1: Failed
+ *
+ ****************************************************************************/
+
+int8_t bl602_dma_channel_request(bl602_dma_callback_t callback, void *arg)
+{
+  struct dma_channel_s *dmach;
+  unsigned int ch;
+  int ret;
+
+  /* Take a count from the channel counting semaphore.  We may block
+   * if there are no free channels.  When we get the count, then we can
+   * be assured that a channel is available in the channel list and is
+   * reserved for us.
+   */
+
+  ret = nxsem_wait_uninterruptible(&g_dmac.chansem);
+  if (ret < 0)
+    {
+      return -1;
+    }
+
+  /* Get exclusive access to the DMA channel list */
+
+  ret = nxsem_wait_uninterruptible(&g_dmac.exclsem);
+  if (ret < 0)
+    {
+      nxsem_post(&g_dmac.chansem);
+      return -1;
+    }
+
+  /* Search for an available DMA channel */
+
+  for (ch = 0, dmach = NULL; ch < BL602_DMA_NCHANNELS; ch++)
+    {
+      struct dma_channel_s *candidate = &g_dmach[ch];
+      if (!candidate->inuse)
+        {
+          dmainfo("DMA Channel %u assigned.\n", ch);
+          dmach        = candidate;
+          dmach->inuse = true;
+
+          break;
+        }
+    }
+
+  nxsem_post(&g_dmac.exclsem);
+
+  /* Since we have reserved a DMA descriptor by taking a count from chansem,
+   * it would be a serious logic failure if we could not find a free channel
+   * for our use.
+   */
+
+  DEBUGASSERT(dmach);
+  dmach->callback = callback;
+  dmach->arg = arg;
+  return dmach->chan;
+}
+
+/****************************************************************************
+ * Name: bl602_dma_channel_release
+ *
+ * Description:
+ *   Release a DMA channel.
+ *
+ * Input Parameters:
+ *   channel: DMA channel.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success. Otherwise -1 (ERROR).
+ *
+ ****************************************************************************/
+
+int bl602_dma_channel_release(uint8_t channel_id)
+{
+  /* Get exclusive access to the DMA channel list */
+
+  if (nxsem_wait_uninterruptible(&g_dmac.exclsem) < 0)
+    {
+      return -1;
+    }
+
+  /* Verify if the channel is actually in use */
+
+  if (g_dmach[channel_id].inuse)
+    {
+      /* This channel was infact in use, release it and increment the
+       * count of free channels for use.
+       */
+
+      g_dmach[channel_id].inuse = false;
+      nxsem_post(&g_dmac.chansem);
+    }
+
+  nxsem_post(&g_dmac.exclsem);
+  return 0;
+}
+
+/****************************************************************************
+ * Name: bl602_dma_channel_start
+ *
+ * Description:
+ *   Start a DMA channel.
+ *
+ * Input Parameters:
+ *   channel: DMA channel.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success. Otherwise -1 (ERROR).
+ *
+ ****************************************************************************/
+
+int bl602_dma_channel_start(uint8_t channel_id)
+{
+  /* Unmask interrupts for:
+   *  - DMA_INT_TCOMPLETED
+   *  - DMA_INT_ERR
+   * Enable Terminal Count interrupt.
+   */
+
+  modifyreg32(BL602_DMA_CH_N_REG(BL602_DMA_CONFIG_OFFSET, channel_id),
+              DMA_C0CONFIG_ITC | DMA_C0CONFIG_IE,
+              0);
+  modifyreg32(BL602_DMA_CH_N_REG(BL602_DMA_CONTROL_OFFSET, channel_id),
+              0,
+              DMA_C0CONTROL_I);
+
+  /* Enable channel */
+
+  modifyreg32(BL602_DMA_CH_N_REG(BL602_DMA_CONFIG_OFFSET, channel_id),
+              0,
+              DMA_C0CONFIG_E);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: bl602_dma_channel_stop
+ *
+ * Description:
+ *   Stop a DMA channel.
+ *
+ * Input Parameters:
+ *   channel: DMA channel.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success. Otherwise -1 (ERROR).
+ *
+ ****************************************************************************/
+
+int bl602_dma_channel_stop(uint8_t channel_id)
+{
+  /* Disable channel */
+
+  modifyreg32(BL602_DMA_CH_N_REG(BL602_DMA_CONFIG_OFFSET, channel_id),
+              DMA_C0CONFIG_E,
+              0);
+
+  /* Mask interrupts for:
+   *  - DMA_INT_TCOMPLETED
+   *  - DMA_INT_ERR
+   */
+
+  modifyreg32(BL602_DMA_CH_N_REG(BL602_DMA_CONFIG_OFFSET, channel_id),
+              0,
+              DMA_C0CONFIG_ITC | DMA_C0CONFIG_IE);
+
+  /* Clear interrupts for channel in:
+   *  - DMA_INTTCCLEAR
+   *  - DMA_INTERRORSTATUS
+   */
+
+  putreg32((1 << channel_id), BL602_DMA_INTTCCLEAR);
+  putreg32((1 << channel_id), BL602_DMA_INTERRCLR);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: riscv_dma_initialize
+ *
+ * Description:
+ *   Intialize DMA controller.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void weak_function riscv_dma_initialize(void)
+{
+  uint8_t ch;
+
+  #ifdef CONFIG_DEBUG_DMA_INFO
+    struct bl602_dmaregs_s regs;
+  #endif
+
+  dmainfo("Initialize DMA\n");
+
+  /* Note we may want to set EN bits in DMAEN as part of clk_cfg2.
+   * At reset these bits are already set to enabled, and the documentation
+   * is a little thin around this.  If we implement more low power
+   * configuration we will want to be more clear about these bits.
+   */
+
+  /* Initialize the channel list  */
+
+  nxsem_init(&g_dmac.exclsem, 0, 1);
+  nxsem_init(&g_dmac.chansem, 0, BL602_DMA_NCHANNELS);
+
+  for (ch = 0; ch < BL602_DMA_NCHANNELS; ch++)
+    {
+      g_dmach[ch].chan = ch;
+
+      /* Disable the DMA channel */
+
+      putreg32(0, BL602_DMA_CH_N_REG(BL602_DMA_CONFIG_OFFSET, ch));
+    }
+
+  /* Attach DMA tranfer complete interrupt handler */
+
+  irq_attach(BL602_IRQ_DMA_ALL, bl602_dma_int_handler, NULL);
+  up_enable_irq(BL602_IRQ_DMA_ALL);
+
+  /* Enable SMDMA controller */
+
+  modifyreg32(BL602_DMA_TOP_CONFIG, 0, DMA_TOP_CONFIG_E);
+
+  /* Dump DMA register state */
+
+  bl602_dmasample(&regs);
+  bl602_dmadump(&regs, "Initialized DMA");
+}
+
+/****************************************************************************
+ * Name: bl602_dmasample
+ *
+ * Description:
+ *   Sample DMA register contents
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+void bl602_dmasample(struct bl602_dmaregs_s *regs)
+{
+  irqstate_t flags;
+
+  /* Sample DMA registers. */
+
+  flags              = enter_critical_section();
+
+  regs->intstatus         = getreg32(BL602_DMA_INTSTATUS);
+  regs->inttcstatus       = getreg32(BL602_DMA_INTTCSTATUS);
+  regs->inttcclear        = getreg32(BL602_DMA_INTTCCLEAR);
+  regs->interrorstatus    = getreg32(BL602_DMA_INTERRORSTATUS);
+  regs->interrclr         = getreg32(BL602_DMA_INTERRCLR);
+  regs->rawinttcstatus    = getreg32(BL602_DMA_RAWINTTCSTATUS);
+  regs->rawinterrorstatus = getreg32(BL602_DMA_RAWINTERRORSTATUS);
+  regs->enbldchns         = getreg32(BL602_DMA_ENBLDCHNS);
+  regs->softbreq          = getreg32(BL602_DMA_SOFTBREQ);
+  regs->softsreq          = getreg32(BL602_DMA_SOFTSREQ);
+  regs->softlbreq         = getreg32(BL602_DMA_SOFTLBREQ);
+  regs->softlsreq         = getreg32(BL602_DMA_SOFTLSREQ);
+  regs->top_config        = getreg32(BL602_DMA_TOP_CONFIG);
+  regs->sync              = getreg32(BL602_DMA_SYNC);
+  regs->c0srcaddr         = getreg32(BL602_DMA_C0SRCADDR);
+  regs->c0dstaddr         = getreg32(BL602_DMA_C0DSTADDR);
+  regs->c0lli             = getreg32(BL602_DMA_C0LLI);
+  regs->c0control         = getreg32(BL602_DMA_C0CONTROL);
+  regs->c0config          = getreg32(BL602_DMA_C0CONFIG);
+  regs->c1srcaddr         = getreg32(BL602_DMA_C1SRCADDR);
+  regs->c1dstaddr         = getreg32(BL602_DMA_C1DSTADDR);
+  regs->c1lli             = getreg32(BL602_DMA_C1LLI);
+  regs->c1control         = getreg32(BL602_DMA_C1CONTROL);
+  regs->c1config          = getreg32(BL602_DMA_C1CONFIG);
+  regs->c2srcaddr         = getreg32(BL602_DMA_C2SRCADDR);
+  regs->c2dstaddr         = getreg32(BL602_DMA_C2DSTADDR);
+  regs->c2lli             = getreg32(BL602_DMA_C2LLI);
+  regs->c2control         = getreg32(BL602_DMA_C2CONTROL);
+  regs->c2config          = getreg32(BL602_DMA_C2CONFIG);
+  regs->c3srcaddr         = getreg32(BL602_DMA_C3SRCADDR);
+  regs->c3dstaddr         = getreg32(BL602_DMA_C3DSTADDR);
+  regs->c3lli             = getreg32(BL602_DMA_C3LLI);
+  regs->c3control         = getreg32(BL602_DMA_C3CONTROL);
+  regs->c3config          = getreg32(BL602_DMA_C3CONFIG);
+
+  leave_critical_section(flags);
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_dmadump
+ *
+ * Description:
+ *   Dump previously sampled DMA register contents
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+void bl602_dmadump(const struct bl602_dmaregs_s *regs,
+                   const char *msg)
+{
+  dmainfo("%s\n", msg);
+  dmainfo("  DMA Registers:\n");
+  dmainfo("   INTSTATUS: %08x\n", regs->intstatus);
+  dmainfo("  INTTCSTATU: %08x\n", regs->inttcstatus);
+  dmainfo("  INTTCCLEAR: %08x\n", regs->inttcclear);
+  dmainfo("  INTERRORST: %08x\n", regs->interrorstatus);
+  dmainfo("   INTERRCLR: %08x\n", regs->interrclr);
+  dmainfo("  RAWINTTCST: %08x\n", regs->rawinttcstatus);
+  dmainfo("  RAWINTERRO: %08x\n", regs->rawinterrorstatus);
+  dmainfo("   ENBLDCHNS: %08x\n", regs->enbldchns);
+  dmainfo("    SOFTBREQ: %08x\n", regs->softbreq);
+  dmainfo("    SOFTSREQ: %08x\n", regs->softsreq);
+  dmainfo("   SOFTLBREQ: %08x\n", regs->softlbreq);
+  dmainfo("   SOFTLSREQ: %08x\n", regs->softlsreq);
+  dmainfo("  TOP_CONFIG: %08x\n", regs->top_config);
+  dmainfo("        SYNC: %08x\n", regs->sync);
+  dmainfo("  === Channel 0 ===\n");
+  dmainfo("   C0SRCADDR: %08x\n", regs->c0srcaddr);
+  dmainfo("   C0DSTADDR: %08x\n", regs->c0dstaddr);
+  dmainfo("       C0LLI: %08x\n", regs->c0lli);
+  dmainfo("   C0CONTROL: %08x\n", regs->c0control);
+  dmainfo("    C0CONFIG: %08x\n", regs->c0config);
+  dmainfo("  === Channel 1 ===\n");
+  dmainfo("   C1SRCADDR: %08x\n", regs->c1srcaddr);
+  dmainfo("   C1DSTADDR: %08x\n", regs->c1dstaddr);
+  dmainfo("       C1LLI: %08x\n", regs->c1lli);
+  dmainfo("   C1CONTROL: %08x\n", regs->c1control);
+  dmainfo("    C1CONFIG: %08x\n", regs->c1config);
+  dmainfo("  === Channel 2 ===\n");
+  dmainfo("   C2SRCADDR: %08x\n", regs->c2srcaddr);
+  dmainfo("   C2DSTADDR: %08x\n", regs->c2dstaddr);
+  dmainfo("       C2LLI: %08x\n", regs->c2lli);
+  dmainfo("   C2CONTROL: %08x\n", regs->c2control);
+  dmainfo("    C2CONFIG: %08x\n", regs->c2config);
+  dmainfo("  === Channel 3 ===\n");
+  dmainfo("   C3SRCADDR: %08x\n", regs->c3srcaddr);
+  dmainfo("   C3DSTADDR: %08x\n", regs->c3dstaddr);
+  dmainfo("       C3LLI: %08x\n", regs->c3lli);
+  dmainfo("   C3CONTROL: %08x\n", regs->c3control);
+  dmainfo("    C3CONFIG: %08x\n", regs->c3config);
+}
+#endif
diff --git a/arch/risc-v/src/bl602/bl602_dma.h b/arch/risc-v/src/bl602/bl602_dma.h
new file mode 100644
index 0000000000..d9e535c238
--- /dev/null
+++ b/arch/risc-v/src/bl602/bl602_dma.h
@@ -0,0 +1,262 @@
+/****************************************************************************
+ * arch/risc-v/src/bl602/bl602_dma.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_BL602_BL602_DMA_H
+#define __ARCH_RISCV_SRC_BL602_BL602_DMA_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define BL602_DMA_NCHANNELS    4
+#define BL602_DMA_INT_EVT_TC  0 /* Interrupt callback status Transfer Complete */
+#define BL602_DMA_INT_EVT_ERR 1 /* Interrupt callback status Error */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+struct bl602_dmaregs_s
+{
+  uint32_t intstatus;         /* Interrupt status. */
+  uint32_t inttcstatus;       /* Interrupt terminal count request status. */
+  uint32_t inttcclear;        /* Terminal count request clear. */
+  uint32_t interrorstatus;    /* Interrupt error status. */
+  uint32_t interrclr;         /* Interrupt error clear. */
+  uint32_t rawinttcstatus;    /* Status of the terminal count interrupt prior to masking. */
+  uint32_t rawinterrorstatus; /* Status of the error interrupt prior to masking. */
+  uint32_t enbldchns;         /* Channel enable status. */
+  uint32_t softbreq;          /* Software burst request. */
+  uint32_t softsreq;          /* Software single request. */
+  uint32_t softlbreq;         /* Software last burst request. */
+  uint32_t softlsreq;         /* Software last single request. */
+  uint32_t top_config;        /* DMA general configuration. */
+  uint32_t sync;              /* DMA request asynchronous setting. */
+  uint32_t c0srcaddr;         /* Channel DMA source address. */
+  uint32_t c0dstaddr;         /* Channel DMA Destination address. */
+  uint32_t c0lli;             /* Channel DMA link list. */
+  uint32_t c0control;         /* Channel DMA bus control. */
+  uint32_t c0config;          /* Channel DMA configuration. */
+  uint32_t c1srcaddr;         /* Channel DMA source address. */
+  uint32_t c1dstaddr;         /* Channel DMA Destination address. */
+  uint32_t c1lli;             /* Channel DMA link list. */
+  uint32_t c1control;         /* Channel DMA bus control. */
+  uint32_t c1config;          /* Channel DMA configuration. */
+  uint32_t c2srcaddr;         /* Channel DMA source address. */
+  uint32_t c2dstaddr;         /* Channel DMA Destination address. */
+  uint32_t c2lli;             /* Channel DMA link list. */
+  uint32_t c2control;         /* Channel DMA bus control. */
+  uint32_t c2config;          /* Channel DMA configuration. */
+  uint32_t c3srcaddr;         /* Channel DMA source address. */
+  uint32_t c3dstaddr;         /* Channel DMA Destination address. */
+  uint32_t c3lli;             /* Channel DMA link list. */
+  uint32_t c3control;         /* Channel DMA bus control. */
+  uint32_t c3config;          /* Channel DMA configuration. */
+};
+#endif
+
+/* Configuration for the LLI Control entry, defined as matching
+ * the DMA channel control register.
+ */
+
+begin_packed_struct struct bl602_dma_ctrl_s
+{
+  uint32_t transfer_size  : 12;
+  uint32_t src_burst_size : 3;
+  uint32_t dst_burst_size : 3;
+  uint32_t src_width      : 3;
+  uint32_t dst_width      : 3;
+  uint32_t sld            : 1;
+  uint32_t rsvd           : 1;
+  uint32_t src_increment  : 1;
+  uint32_t dst_increment  : 1;
+  uint32_t protect        : 3;
+  uint32_t tc_int_en      : 1;
+} end_packed_struct;
+
+/* Configuration for the LLI packed data.
+ * This must be aligned to uint32 pointer.
+ */
+
+begin_packed_struct struct aligned_data(4) bl602_lli_ctrl_s
+{
+  uint32_t src_addr;
+  uint32_t dst_addr;
+  uint32_t next_lli;                /* Address for next LLI entry */
+  struct bl602_dma_ctrl_s dma_ctrl; /* Control register config for entry */
+} end_packed_struct;
+
+/* Description:
+ *   This is the type of the callback that is used to inform the user of the
+ *   completion of the DMA.
+ *
+ * Input Parameters:
+ *   channel - Refers to the DMA channel
+ *   status  - A bit encoded value that provides the completion status.
+ *             See the BL602_DMA_INT_EVT_* definitions above.
+ *   arg     - A user-provided value.
+ */
+
+typedef void (*bl602_dma_callback_t) \
+  (uint8_t channel, uint8_t status, void *arg);
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: bl602_dma_channel_request
+ *
+ * Description:
+ *   Allocate a new DMA channel.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   0-3: DMA channel
+ *   -1: Failed
+ *
+ ****************************************************************************/
+
+int8_t bl602_dma_channel_request(bl602_dma_callback_t callback, void *arg);
+
+/****************************************************************************
+ * Name: bl602_dma_channel_release
+ *
+ * Description:
+ *   Release a DMA channel.
+ *
+ * Input Parameters:
+ *   channel: DMA channel.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success. Otherwise -1 (ERROR).
+ *
+ ****************************************************************************/
+
+int bl602_dma_channel_release(uint8_t channel);
+
+/****************************************************************************
+ * Name: bl602_dma_channel_start
+ *
+ * Description:
+ *   Start a DMA channel.
+ *
+ * Input Parameters:
+ *   channel: DMA channel.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success. Otherwise -1 (ERROR).
+ *
+ ****************************************************************************/
+
+int bl602_dma_channel_start(uint8_t channel_id);
+
+/****************************************************************************
+ * Name: bl602_dma_channel_stop
+ *
+ * Description:
+ *   Stop a DMA channel.
+ *
+ * Input Parameters:
+ *   channel: DMA channel.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success. Otherwise -1 (ERROR).
+ *
+ ****************************************************************************/
+
+int bl602_dma_channel_stop(uint8_t channel_id);
+
+/****************************************************************************
+ * Name: bl602_dma_init
+ *
+ * Description:
+ *   Intialize DMA controller.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success. Otherwise -1 (ERROR).
+ *
+ ****************************************************************************/
+
+int bl602_dma_init(void);
+
+/****************************************************************************
+ * Name: bl602_dmasample
+ *
+ * Description:
+ *   Sample DMA register contents
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+void bl602_dmasample(struct bl602_dmaregs_s *regs);
+#else
+#  define bl602_dmasample(regs)
+#endif
+
+/****************************************************************************
+ * Name: bl602_dmadump
+ *
+ * Description:
+ *   Dump previously sampled DMA register contents
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA_INFO
+void bl602_dmadump(const struct bl602_dmaregs_s *regs, const char *msg);
+#else
+#  define bl602_dmadump(regs,msg)
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_RISCV_SRC_BL602_BL602_DMA_H */
diff --git a/arch/risc-v/src/bl602/bl602_romapi.h b/arch/risc-v/src/bl602/bl602_romapi.h
index f9c17011d8..7f45c66adc 100644
--- a/arch/risc-v/src/bl602/bl602_romapi.h
+++ b/arch/risc-v/src/bl602/bl602_romapi.h
@@ -31,6 +31,7 @@
 #define BL602_ROMAPI_FUNC(idx) (*(uintptr_t *)(BL602_ROMAPI_BASE + (idx)*4))
 
 #define BL602_ROMAPI_ASM_DELAY_US              BL602_ROMAPI_FUNC(20)
+#define BL602_ROMAPI_MEMCPY4                   BL602_ROMAPI_FUNC(24)
 #define BL602_ROMAPI_EFUSE_CTRL_LOAD_R0        BL602_ROMAPI_FUNC(31)
 #define BL602_ROMAPI_RST_SYSTEM                BL602_ROMAPI_FUNC(47)
 #define BL602_ROMAPI_RST_CPU_SW                BL602_ROMAPI_FUNC(48)
@@ -43,4 +44,6 @@
 #define BL602_ROMAPI_SFLASH_WRITE_WITHLOCK     BL602_ROMAPI_FUNC(171)
 #define BL602_ROMAPI_SFLASH_EREASE_WITHLOCK    BL602_ROMAPI_FUNC(172)
 
+#define bl602_romapi_memcpy_4 ((uint32_t*  (*) (uint32_t*, const uint32_t*, uint32_t))BL602_ROMAPI_MEMCPY4)
+
 #endif /* __ARCH_RISCV_SRC_BL602_BL602_ROMAPI_H */
diff --git a/arch/risc-v/src/bl602/bl602_spi.c b/arch/risc-v/src/bl602/bl602_spi.c
index c2d9faa3b3..45740432cc 100644
--- a/arch/risc-v/src/bl602/bl602_spi.c
+++ b/arch/risc-v/src/bl602/bl602_spi.c
@@ -33,15 +33,22 @@
 #include <sys/types.h>
 
 #include <nuttx/arch.h>
+#include <nuttx/compiler.h>
+
 #include <nuttx/irq.h>
+#include <nuttx/kmalloc.h>
 #include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
 #include <nuttx/spi/spi.h>
 
 #include <arch/board/board.h>
 
 #include "bl602_glb.h"
 #include "bl602_gpio.h"
+#include "bl602_romapi.h"
 #include "bl602_spi.h"
+#include "bl602_dma.h"
+#include "hardware/bl602_dma.h"
 #include "hardware/bl602_glb.h"
 #include "hardware/bl602_spi.h"
 #include "hardware/bl602_hbn.h"
@@ -52,6 +59,7 @@
  ****************************************************************************/
 
 #define SPI_FREQ_DEFAULT 400000
+#define LLI_BUFF_SIZE 2048  /* Maximum transaction count per LLI entry. */
 
 /****************************************************************************
  * Private Types
@@ -126,7 +134,8 @@ struct bl602_spi_priv_s
 
   /* Interrupt wait semaphore */
 
-  sem_t sem_isr;
+  sem_t sem_isr_tx;
+  sem_t sem_isr_rx;
 
   uint32_t frequency; /* Requested clock frequency */
   uint32_t actual;    /* Actual clock frequency */
@@ -136,7 +145,8 @@ struct bl602_spi_priv_s
   /* Actual SPI send/receive bits once transmission */
 
   uint8_t nbits;
-  uint8_t dma_chan;
+  int8_t dma_txchan;
+  int8_t dma_rxchan;
 };
 
 /****************************************************************************
@@ -227,7 +237,9 @@ static struct bl602_spi_priv_s bl602_spi_priv =
               {
                 .ops = &bl602_spi_ops
               },
-  .config = &bl602_spi_config
+  .config = &bl602_spi_config,
+  .dma_rxchan = -1,
+  .dma_txchan = -1,
 };
 
 #endif  /* CONFIG_BL602_SPI0 */
@@ -236,6 +248,38 @@ static struct bl602_spi_priv_s bl602_spi_priv =
  * Private Functions
  ****************************************************************************/
 
+#ifdef CONFIG_BL602_SPI_DMA
+static void bl602_dma_rx_callback(uint8_t channel, uint8_t status, void *arg)
+{
+  struct bl602_spi_priv_s *priv = (struct bl602_spi_priv_s *)arg;
+  UNUSED(channel);
+  spiinfo("RX interrupt fired with status %u\n", status);
+  if (status == BL602_DMA_INT_EVT_TC)
+    {
+      nxsem_post(&priv->sem_isr_rx);
+    }
+  else
+    {
+      spierr("DMA transfer failed for RX.\n");
+    }
+}
+
+static void bl602_dma_tx_callback(uint8_t channel, uint8_t status, void *arg)
+{
+  struct bl602_spi_priv_s *priv = (struct bl602_spi_priv_s *)arg;
+  UNUSED(channel);
+  spiinfo("TX interrupt fired with status %u\n", status);
+  if (status == BL602_DMA_INT_EVT_TC)
+    {
+      nxsem_post(&priv->sem_isr_tx);
+    }
+  else
+    {
+      spierr("DMA transfer failed for TX.\n");
+    }
+}
+#endif
+
 /****************************************************************************
  * Name: bl602_check_with_new_prescale
  *
@@ -773,6 +817,168 @@ static int bl602_spi_hwfeatures(struct spi_dev_s *dev,
 }
 #endif
 
+/****************************************************************************
+ * Name: lli_list_init
+ *
+ * Description:
+ *   Configure the LLI structure for DMA transactions with SPI
+ *
+ * Input Parameters:
+ *   priv    - Device-specific state data
+ *   tx_lli - A pointer to the LLI structures for TX.
+ *   rx_lli - A pointer to the LLI structures for RX.
+ *   tx_buffer - A pointer to the transaction buffer for TX.
+ *   rx_buffer - A pointer to the transaction buffer for RX.
+ *               Buffer is null if TX only.
+ *   nwords - the length of data to send from the buffer in number of words.
+ *            The wordsize is determined by the number of bits-per-word
+ *            selected for the SPI interface.  If nbits <= 8, the data is
+ *            packed into uint8_t's; if nbits >8, the data is packed into
+ *            uint16_t's
+ *
+ * Returned Value:
+ *   Error state - 0 on success.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_BL602_SPI_DMA
+static int lli_list_init(struct bl602_spi_priv_s *priv,
+                         struct bl602_lli_ctrl_s **tx_lli,
+                         struct bl602_lli_ctrl_s **rx_lli,
+                         const void *txbuffer,
+                         void *rxbuffer, size_t nwords)
+{
+  uint8_t i;
+  uint32_t count;
+  uint32_t remainder;
+  struct bl602_dma_ctrl_s  dma_ctrl;
+
+  /* Determine the how many LLI entries will be required */
+
+  count = (nwords * priv->nbits / 8) / LLI_BUFF_SIZE;
+  remainder = (nwords * priv->nbits / 8) % LLI_BUFF_SIZE;
+
+  /* If LLI entry cannot be fully packed with data, add an additional entry
+   * for the remainder entry.
+   */
+
+  if (remainder != 0)
+    {
+      count = count + 1;
+    }
+
+  /* Set the base config that will be used for each entry */
+
+  dma_ctrl.src_width = (priv->nbits / 8) - 1; /* 8/16/32 bits */
+  dma_ctrl.dst_width = dma_ctrl.src_width;
+  dma_ctrl.src_burst_size = 0; /* 1 item per transaction */
+  dma_ctrl.dst_burst_size = 0; /* 1 item per transaction */
+  dma_ctrl.protect = 0;
+  dma_ctrl.tc_int_en = 0; /* We will overwrite this in the last entry */
+  dma_ctrl.rsvd = 0;
+  dma_ctrl.sld = 0; /* Not used for non mem-to-mem transfers. */
+
+  /* We will set these per entry:
+   *  - transfer_size
+   *  - src_increment
+   *  - dst_increment
+   */
+
+  /* Allocate the transfer block.
+   * TODO consider supporting pre-allocation of these structures.
+   * most transaction will only use a single LLI, so we could
+   * actually place the single LLI structure on the stack (4*4 bytes)
+   */
+
+  *tx_lli = kmm_malloc(sizeof(struct bl602_lli_ctrl_s) * count);
+  if (*tx_lli == NULL)
+    {
+      spierr("Failed to allocate lli for tx.\n");
+      return -1;
+    }
+
+  if (rxbuffer != NULL)
+    {
+      *rx_lli = kmm_malloc(sizeof(struct bl602_lli_ctrl_s) * count);
+      if (*rx_lli == NULL)
+        {
+          spierr("Failed to allocate lli for rx.\n");
+          kmm_free(*tx_lli);
+          return -1;
+        }
+    }
+  else
+    {
+      *rx_lli = NULL;
+    }
+
+  for (i = 0; i < count; i++)
+    {
+      /* Check if this is the final entry and there is remainder set */
+
+      if ((i == (count - 1)) && (remainder != 0))
+        {
+          dma_ctrl.transfer_size = remainder;
+        }
+      else
+        {
+          dma_ctrl.transfer_size = LLI_BUFF_SIZE;
+        }
+
+      /* Configure tx side */
+
+        {
+          dma_ctrl.dst_increment = 0;
+          dma_ctrl.src_increment = 1;
+          (*tx_lli)[i].dma_ctrl = dma_ctrl;
+          (*tx_lli)[i].dst_addr = BL602_SPI_FIFO_WDATA;
+          (*tx_lli)[i].src_addr = \
+            (uint32_t)txbuffer + (dma_ctrl.src_width * i * LLI_BUFF_SIZE);
+
+          /* Assume last entry, we will overwrite as needed. */
+
+          (*tx_lli)[i].next_lli = 0;
+
+          /* Link entry */
+
+          if (i != 0)
+            {
+              (*tx_lli)[i - 1].next_lli = (uint32_t)&(*tx_lli)[i];
+            }
+        }
+
+      /* Configure rx side */
+
+      if (rxbuffer != NULL)
+        {
+          dma_ctrl.dst_increment = 1;
+          dma_ctrl.src_increment = 0;
+          (*rx_lli)[i].dma_ctrl = dma_ctrl;
+          (*rx_lli)[i].dst_addr = \
+            (uint32_t)rxbuffer + (dma_ctrl.dst_width * i * LLI_BUFF_SIZE);
+          (*rx_lli)[i].src_addr = BL602_SPI_FIFO_RDATA;
+          (*rx_lli)[i].next_lli = 0; /* Assume last entry, we will overwrite as needed. */
+
+          /* Link entry */
+
+          if (i != 0)
+            {
+              (*rx_lli)[i - 1].next_lli = (uint32_t)&(*rx_lli)[i];
+            }
+        }
+    }
+
+  (*tx_lli)[count - 1].dma_ctrl.tc_int_en = 1;
+
+  if (rxbuffer != NULL)
+    {
+      (*rx_lli)[count - 1].dma_ctrl.tc_int_en = 1;
+    }
+
+  return 0;
+}
+#endif
+
 /****************************************************************************
  * Name: bl602_spi_dma_exchange
  *
@@ -794,13 +1000,97 @@ static int bl602_spi_hwfeatures(struct spi_dev_s *dev,
  *
  ****************************************************************************/
 
+#ifdef CONFIG_BL602_SPI_DMA
 static void bl602_spi_dma_exchange(struct bl602_spi_priv_s *priv,
                                    const void *txbuffer,
                                    void *rxbuffer, uint32_t nwords)
 {
-  spierr("SPI dma not supported\n");
-  DEBUGPANIC();
+  int err;
+
+  #ifdef CONFIG_DEBUG_DMA_INFO
+    struct bl602_dmaregs_s regs;
+  #endif
+
+  struct bl602_lli_ctrl_s *tx_lli;
+  struct bl602_lli_ctrl_s *rx_lli;
+
+  /* Enable master */
+
+  modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_S_EN, SPI_CFG_CR_M_EN);
+
+  err = lli_list_init(priv, &tx_lli, &rx_lli, txbuffer, rxbuffer, nwords);
+
+  if (err < 0)
+    {
+      spierr("Failed to initalize DMA LLI\n");
+      return;
+    }
+
+  /* Configure DMA controller with LLI.
+   * This needs to be set using the ROM API from TCM to initialize all 4
+   * registers from the LLI structure.
+   */
+
+  bl602_romapi_memcpy_4(
+    (uint32_t *)BL602_DMA_CH_N_REG(BL602_DMA_SRCADDR_OFFSET,
+                                   priv->dma_txchan),
+    (uint32_t *)tx_lli, 4);
+
+  if (rxbuffer != NULL)
+    {
+      bl602_romapi_memcpy_4(
+        (uint32_t *)BL602_DMA_CH_N_REG(BL602_DMA_SRCADDR_OFFSET,
+                                      priv->dma_rxchan),
+        (uint32_t *)rx_lli, 4);
+    }
+
+  /* Dump DMA register state */
+
+  bl602_dmasample(&regs);
+  bl602_dmadump(&regs, "Initialized DMA");
+
+  /* Start channel */
+
+  if (rxbuffer != NULL)
+    {
+      bl602_dma_channel_start(priv->dma_rxchan);
+    }
+
+  bl602_dma_channel_start(priv->dma_txchan);
+
+  /* Dump DMA register state */
+
+  bl602_dmasample(&regs);
+  bl602_dmadump(&regs, "Post Start DMA");
+
+  /* Wait for RX and TX to complete. */
+
+  nxsem_wait_uninterruptible(&priv->sem_isr_tx);
+
+  if (rxbuffer != NULL)
+    {
+      nxsem_wait_uninterruptible(&priv->sem_isr_rx);
+    }
+
+  /* Stop channels */
+
+  bl602_dma_channel_stop(priv->dma_txchan);
+
+  if (rxbuffer != NULL)
+    {
+      bl602_dma_channel_stop(priv->dma_rxchan);
+    }
+
+  kmm_free(tx_lli);
+
+  if (rx_lli != NULL)
+    {
+      kmm_free(rx_lli);
+    }
+
+  return;
 }
+#endif
 
 /****************************************************************************
  * Name: bl602_spi_poll_send
@@ -869,6 +1159,7 @@ static uint32_t bl602_spi_poll_send(struct bl602_spi_priv_s *priv,
  *
  ****************************************************************************/
 
+#ifdef CONFIG_BL602_SPI_DMA
 static uint32_t bl602_spi_dma_send(struct bl602_spi_priv_s *priv,
                                    uint32_t wd)
 {
@@ -878,6 +1169,7 @@ static uint32_t bl602_spi_dma_send(struct bl602_spi_priv_s *priv,
 
   return rd;
 }
+#endif
 
 /****************************************************************************
  * Name: bl602_spi_send
@@ -898,9 +1190,11 @@ static uint32_t bl602_spi_dma_send(struct bl602_spi_priv_s *priv,
 static uint32_t bl602_spi_send(struct spi_dev_s *dev, uint32_t wd)
 {
   struct bl602_spi_priv_s *priv = (struct bl602_spi_priv_s *)dev;
+
+#ifdef CONFIG_BL602_SPI_DMA
   uint32_t rd;
 
-  if (priv->dma_chan)
+  if (priv->dma_txchan >= 0 && priv->dma_rxchan >= 0)
     {
       rd = bl602_spi_dma_send(priv, wd);
     }
@@ -910,6 +1204,9 @@ static uint32_t bl602_spi_send(struct spi_dev_s *dev, uint32_t wd)
     }
 
   return rd;
+#else
+  return bl602_spi_poll_send(priv, wd);
+#endif
 }
 
 /****************************************************************************
@@ -998,7 +1295,8 @@ static void bl602_spi_exchange(struct spi_dev_s *dev,
 {
   struct bl602_spi_priv_s *priv = (struct bl602_spi_priv_s *)dev;
 
-  if (priv->dma_chan)
+#ifdef CONFIG_BL602_SPI_DMA
+  if (priv->dma_txchan >= 0 && priv->dma_rxchan >= 0)
     {
       bl602_spi_dma_exchange(priv, txbuffer, rxbuffer, nwords);
     }
@@ -1006,6 +1304,9 @@ static void bl602_spi_exchange(struct spi_dev_s *dev,
     {
       bl602_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords);
     }
+#else
+  bl602_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords);
+#endif
 }
 
 #ifndef CONFIG_SPI_EXCHANGE
@@ -1145,6 +1446,100 @@ static void bl602_swap_spi_0_mosi_with_miso(uint8_t swap)
     }
 }
 
+#ifdef CONFIG_BL602_SPI_DMA
+/****************************************************************************
+ * Name: bl602_spi_dma_init
+ *
+ * Description:
+ *   Initialize BL SPI connection to DMA engine.
+ *
+ * Input Parameters:
+ *   dev      - Device-specific state data
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void bl602_spi_dma_init(struct spi_dev_s *dev)
+{
+  struct bl602_spi_priv_s *priv = (struct bl602_spi_priv_s *)dev;
+
+  #ifdef CONFIG_DEBUG_DMA_INFO
+    struct bl602_dmaregs_s regs;
+  #endif
+
+  /* NOTE DMA channels are limited on this device and not all SPI devices
+   * care about the RX side.  Consider making the RX channel optional.
+   */
+
+  /* Request a DMA channel for SPI peripheral */
+
+  priv->dma_rxchan = bl602_dma_channel_request(bl602_dma_rx_callback,
+                                               (void *)priv);
+  if (priv->dma_rxchan < 0)
+    {
+      spierr("Failed to allocate GDMA channel\n");
+
+      DEBUGASSERT(false);
+      return;
+    }
+
+  priv->dma_txchan = bl602_dma_channel_request(bl602_dma_tx_callback,
+                                               (void *)priv);
+  if (priv->dma_txchan < 0)
+    {
+      spierr("Failed to allocate GDMA channel\n");
+
+      /* Release the RX channel since we won't be using it. */
+
+      bl602_dma_channel_release(priv->dma_rxchan);
+      priv->dma_rxchan = -1;
+
+      DEBUGASSERT(false);
+      return;
+    }
+
+  /* Configure channels for SPI DMA */
+
+  /* Configure channel from SPI to Mem */
+
+  modifyreg32(
+    BL602_DMA_CH_N_REG(BL602_DMA_CONFIG_OFFSET, priv->dma_rxchan),
+    DMA_C0CONFIG_FLOWCNTRL_MASK | \
+    DMA_C0CONFIG_DSTPERIPHERAL_MASK | \
+    DMA_C0CONFIG_SRCPERIPHERAL_MASK,
+    (BL602_DMA_TRNS_P2M << DMA_C0CONFIG_FLOWCNTRL_SHIFT) | \
+    (BL602_DMA_REQ_SPI_RX << DMA_C0CONFIG_SRCPERIPHERAL_SHIFT));
+
+  /* Configure channel from Mem to SPI */
+
+  modifyreg32(
+    BL602_DMA_CH_N_REG(BL602_DMA_CONFIG_OFFSET, priv->dma_txchan),
+    DMA_C0CONFIG_FLOWCNTRL_MASK | DMA_C0CONFIG_DSTPERIPHERAL_MASK | \
+    DMA_C0CONFIG_SRCPERIPHERAL_MASK,
+    (BL602_DMA_TRNS_M2P << DMA_C0CONFIG_FLOWCNTRL_SHIFT) | \
+    (BL602_DMA_REQ_SPI_TX << DMA_C0CONFIG_DSTPERIPHERAL_SHIFT));
+
+  /* Set FIFO threshold to trigger DMA */
+
+  modifyreg32(
+    BL602_SPI_FIFO_CFG_1,
+    SPI_FIFO_CFG_1_RX_TH_MASK | SPI_FIFO_CFG_1_TX_TH_MASK,
+    (0 << SPI_FIFO_CFG_1_RX_TH_SHIFT) | (0 << SPI_FIFO_CFG_1_TX_TH_SHIFT));
+
+  /* Enable DMA support */
+
+  modifyreg32(BL602_SPI_FIFO_CFG_0, 0,
+              SPI_FIFO_CFG_0_DMA_RX_EN | SPI_FIFO_CFG_0_DMA_TX_EN);
+
+  /* Dump DMA register state */
+
+  bl602_dmasample(&regs);
+  bl602_dmadump(&regs, "Initialized DMA");
+}
+#endif
+
 /****************************************************************************
  * Name: bl602_spi_init
  *
@@ -1167,6 +1562,8 @@ static void bl602_spi_init(struct spi_dev_s *dev)
   /* Initialize the SPI semaphore that enforces mutually exclusive access */
 
   nxsem_init(&priv->exclsem, 0, 1);
+  nxsem_init(&priv->sem_isr_rx, 0, 0);
+  nxsem_init(&priv->sem_isr_tx, 0, 0);
 
   bl602_configgpio(BOARD_SPI_CS);
   bl602_configgpio(BOARD_SPI_MOSI);
@@ -1192,9 +1589,11 @@ static void bl602_spi_init(struct spi_dev_s *dev)
               | SPI_CFG_CR_BYTE_INV | SPI_CFG_CR_BIT_INV,
               SPI_CFG_CR_DEG_EN);
 
-  /* disable rx ignore */
+  /* Disable rx ignore.
+   * Enable continuous mode.
+   */
 
-  modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_RXD_IGNR_EN, 0);
+  modifyreg32(BL602_SPI_CFG, SPI_CFG_CR_RXD_IGNR_EN, SPI_CFG_CR_M_CONT_EN);
 
   bl602_spi_setfrequency(dev, config->clk_freq);
   bl602_spi_setbits(dev, 8);
@@ -1204,6 +1603,10 @@ static void bl602_spi_init(struct spi_dev_s *dev)
 
   modifyreg32(BL602_SPI_FIFO_CFG_0, SPI_FIFO_CFG_0_RX_CLR
               | SPI_FIFO_CFG_0_TX_CLR, 0);
+
+#ifdef CONFIG_BL602_SPI_DMA
+  bl602_spi_dma_init(dev);
+#endif
 }
 
 /****************************************************************************
@@ -1230,7 +1633,8 @@ static void bl602_spi_deinit(struct spi_dev_s *dev)
   priv->actual = 0;
   priv->mode = SPIDEV_MODE0;
   priv->nbits = 0;
-  priv->dma_chan = 0;
+  priv->dma_txchan = -1;
+  priv->dma_rxchan = -1;
 }
 
 /****************************************************************************
@@ -1317,6 +1721,8 @@ int bl602_spibus_uninitialize(struct spi_dev_s *dev)
   bl602_spi_deinit(dev);
 
   nxsem_destroy(&priv->exclsem);
+  nxsem_destroy(&priv->sem_isr_rx);
+  nxsem_destroy(&priv->sem_isr_tx);
 
   return OK;
 }
diff --git a/arch/risc-v/src/bl602/bl602_spi.h b/arch/risc-v/src/bl602/bl602_spi.h
index b2ddb0c226..ddf13f97f2 100644
--- a/arch/risc-v/src/bl602/bl602_spi.h
+++ b/arch/risc-v/src/bl602/bl602_spi.h
@@ -42,8 +42,6 @@ extern "C"
 #define EXTERN extern
 #endif
 
-#ifdef CONFIG_BL602_SPI0
-
 #include <nuttx/spi/spi.h>
 #include <nuttx/spi/spi_transfer.h>
 
@@ -77,8 +75,6 @@ struct spi_dev_s *bl602_spibus_initialize(int port);
 
 int bl602_spibus_uninitialize(struct spi_dev_s *dev);
 
-#endif /* CONFIG_BL602_SPI0 */
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/arch/risc-v/src/bl602/hardware/bl602_dma.h b/arch/risc-v/src/bl602/hardware/bl602_dma.h
index 6d8a89b8f9..67e4a965a4 100644
--- a/arch/risc-v/src/bl602/hardware/bl602_dma.h
+++ b/arch/risc-v/src/bl602/hardware/bl602_dma.h
@@ -106,6 +106,18 @@
 #define BL602_DMA_C3CONTROL          (BL602_DMA_BASE + BL602_DMA_C3CONTROL_OFFSET)
 #define BL602_DMA_C3CONFIG           (BL602_DMA_BASE + BL602_DMA_C3CONFIG_OFFSET)
 
+/* Channel register helpers *************************************************/
+
+#define BL602_DMA_CH_REG_OFFSET             0x000100  /* Offset between channels */
+#define BL602_DMA_CH_REG_BASE               BL602_DMA_C0SRCADDR
+#define BL602_DMA_SRCADDR_OFFSET            0x000000  /* CnSrcAddr */
+#define BL602_DMA_DSTADDR_OFFSET            0x000004  /* CnDstAddr */
+#define BL602_DMA_LLI_OFFSET                0x000008  /* CnLLI */
+#define BL602_DMA_CONTROL_OFFSET            0x00000c  /* CnControl */
+#define BL602_DMA_CONFIG_OFFSET             0x000010  /* CnConfig */
+
+#define BL602_DMA_CH_N_REG(reg, ch) ((BL602_DMA_CH_REG_BASE + (BL602_DMA_CH_REG_OFFSET * ch)) + reg)
+
 /* Register bit definitions *************************************************/
 
 #define DMA_INTSTATUS_MASK              (0xff)
@@ -251,4 +263,23 @@
 #define DMA_C3CONFIG_SRCPERIPHERAL_MASK               (0x1f << DMA_C3CONFIG_SRCPERIPHERAL_SHIFT)
 #define DMA_C3CONFIG_E                                (1 << 0)
 
+#define BL602_DMA_TRNS_M2M (0) /* DMA memory to memory */
+#define BL602_DMA_TRNS_M2P (1) /* DMA memory to peripheral */
+#define BL602_DMA_TRNS_P2M (2) /* DMA peripheral to memory */
+#define BL602_DMA_TRNS_P2P (3) /* DMA peripheral to peripheral */
+
+#define BL602_DMA_REQ_UART0_RX (0)    /* DMA request peripheral UART0 RX */
+#define BL602_DMA_REQ_UART0_TX (1)    /* DMA request peripheral UART0 TX */
+#define BL602_DMA_REQ_UART1_RX (2)    /* DMA request peripheral UART1 RX */
+#define BL602_DMA_REQ_UART1_TX (3)    /* DMA request peripheral UART1 TX */
+#define BL602_DMA_REQ_I2C_RX   (6)    /* DMA request peripheral I2C RX */
+#define BL602_DMA_REQ_I2C_TX   (7)    /* DMA request peripheral I2C TX */
+#define BL602_DMA_REQ_SPI_RX   (10)   /* DMA request peripheral SPI RX */
+#define BL602_DMA_REQ_SPI_TX   (11)   /* DMA request peripheral SPI TX */
+#define BL602_DMA_REQ_I2S_RX   (20)   /* DMA request peripheral SPI RX */
+#define BL602_DMA_REQ_I2S_TX   (21)   /* DMA request peripheral SPI TX */
+#define BL602_DMA_REQ_GPADC0   (22)   /* DMA request peripheral GPADC0 */
+#define BL602_DMA_REQ_GPADC1   (23)   /* DMA request peripheral GPADC1 */
+#define BL602_DMA_REQ_NONE     (0 )   /* DMA request peripheral None */
+
 #endif /* __ARCH_RISCV_SRC_BL602_HARDWARE_BL602_DMA_H */
diff --git a/arch/risc-v/src/common/riscv_initialize.c b/arch/risc-v/src/common/riscv_initialize.c
index d39198b542..0540b16c1e 100644
--- a/arch/risc-v/src/common/riscv_initialize.c
+++ b/arch/risc-v/src/common/riscv_initialize.c
@@ -105,6 +105,19 @@ void up_initialize(void)
   riscv_pminitialize();
 #endif
 
+#ifdef CONFIG_ARCH_DMA
+  /* Initialize the DMA subsystem if the weak function arm_dma_initialize has
+   * been brought into the build
+   */
+
+#ifdef CONFIG_HAVE_WEAKFUNCTIONS
+  if (riscv_dma_initialize)
+#endif
+    {
+      riscv_dma_initialize();
+    }
+#endif
+
   /* Initialize the serial device driver */
 
 #ifdef USE_SERIALDRIVER
diff --git a/arch/risc-v/src/common/riscv_internal.h b/arch/risc-v/src/common/riscv_internal.h
index 861c412cd2..fbfa3f12fa 100644
--- a/arch/risc-v/src/common/riscv_internal.h
+++ b/arch/risc-v/src/common/riscv_internal.h
@@ -232,6 +232,12 @@ void riscv_pminitialize(void);
 #  define riscv_pminitialize()
 #endif
 
+/* DMA **********************************************************************/
+
+#ifdef CONFIG_ARCH_DMA
+void weak_function riscv_dma_initialize(void);
+#endif
+
 /* Low level serial output **************************************************/
 
 void riscv_lowputc(char ch);
diff --git a/boards/risc-v/bl602/bl602evb/configs/dma/defconfig b/boards/risc-v/bl602/bl602evb/configs/dma/defconfig
new file mode 100644
index 0000000000..467b7d51f3
--- /dev/null
+++ b/boards/risc-v/bl602/bl602evb/configs/dma/defconfig
@@ -0,0 +1,82 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_LIBC_FLOATINGPOINT is not set
+# CONFIG_LIBC_LONG_LONG is not set
+# CONFIG_NDEBUG is not set
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDPARMS is not set
+# CONFIG_NSH_DISABLE_LOSMART is not set
+CONFIG_ARCH="risc-v"
+CONFIG_ARCH_BOARD="bl602evb"
+CONFIG_ARCH_BOARD_BL602EVB=y
+CONFIG_ARCH_CHIP="bl602"
+CONFIG_ARCH_CHIP_BL602=y
+CONFIG_ARCH_INTERRUPTSTACK=8192
+CONFIG_ARCH_RISCV=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BINFMT_DISABLE=y
+CONFIG_BL602_DMA=y
+CONFIG_BL602_HAVE_UART0=y
+CONFIG_BL602_I2C0=y
+CONFIG_BL602_I2C_DMA=y
+CONFIG_BL602_SPI0=y
+CONFIG_BL602_SPI_DMA=y
+CONFIG_BL602_TIMER0=y
+CONFIG_BOARD_LOOPSPERMSEC=10000
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_FULLOPT=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_DEV_ZERO=y
+CONFIG_DISABLE_ENVIRON=y
+CONFIG_DISABLE_MQUEUE=y
+CONFIG_DISABLE_POSIX_TIMERS=y
+CONFIG_DISABLE_PSEUDOFS_OPERATIONS=y
+CONFIG_EXAMPLES_HELLO=y
+CONFIG_EXAMPLES_HELLO_STACKSIZE=8192
+CONFIG_EXAMPLES_TIMER=y
+CONFIG_FS_PROCFS=y
+CONFIG_I2C=y
+CONFIG_IDLETHREAD_STACKSIZE=8192
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_INIT_STACKSIZE=8192
+CONFIG_INTELHEX_BINARY=y
+CONFIG_LIBC_PERROR_STDOUT=y
+CONFIG_LIBC_STRERROR=y
+CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_FILEIOSIZE=64
+CONFIG_NSH_LINELEN=64
+CONFIG_NSH_READLINE=y
+CONFIG_NSH_STRERROR=y
+CONFIG_PTHREAD_MUTEX_UNSAFE=y
+CONFIG_PTHREAD_STACK_DEFAULT=8192
+CONFIG_RAM_SIZE=134217728
+CONFIG_RAM_START=0xc0800000
+CONFIG_RAW_BINARY=y
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_WAITPID=y
+CONFIG_SIG_PREALLOC_IRQ_ACTIONS=4
+CONFIG_SPI=y
+CONFIG_STACK_COLORATION=y
+CONFIG_START_DAY=20
+CONFIG_START_MONTH=3
+CONFIG_START_YEAR=2020
+CONFIG_STDIO_DISABLE_BUFFERING=y
+CONFIG_SYSTEM_I2CTOOL=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_SYSTEM_SPITOOL=y
+CONFIG_TASK_NAME_SIZE=12
+CONFIG_TASK_SPAWN_DEFAULT_STACKSIZE=8192
+CONFIG_TESTING_GETPRIME=y
+CONFIG_TIMER=y
+CONFIG_TIMER_ARCH=y
+CONFIG_UART0_BAUD=2000000
+CONFIG_UART0_RXBUFSIZE=128
+CONFIG_UART0_SERIAL_CONSOLE=y
+CONFIG_UART0_TXBUFSIZE=128