aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2012-06-14 18:31:40 -0400
committerMarti Bolivar <mbolivar@leaflabs.com>2012-06-15 17:41:37 -0400
commite72e411c1f0e4f96326351264301c6e475206e30 (patch)
treee5da82ac3cc53b293ff5e88db748d73461b7aa32 /libmaple
parent59b81331e9e767a271b873dc4a594e7406fa36a3 (diff)
downloadlibrambutan-e72e411c1f0e4f96326351264301c6e475206e30.tar.gz
librambutan-e72e411c1f0e4f96326351264301c6e475206e30.zip
Implement DMA tube API on STM32F2.
Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
Diffstat (limited to 'libmaple')
-rw-r--r--libmaple/rules.mk2
-rw-r--r--libmaple/stm32f2/dma.c504
-rw-r--r--libmaple/stm32f2/include/series/dma.h810
-rw-r--r--libmaple/stm32f2/rules.mk1
4 files changed, 1315 insertions, 2 deletions
diff --git a/libmaple/rules.mk b/libmaple/rules.mk
index 0c29ec7..41f5601 100644
--- a/libmaple/rules.mk
+++ b/libmaple/rules.mk
@@ -13,9 +13,7 @@ CFLAGS_$(d) = $(LIBMAPLE_PRIVATE_INCLUDES) $(LIBMAPLE_INCLUDES) -Wall -Werror
# Local rules and targets
cSRCS_$(d) := adc.c
cSRCS_$(d) += dac.c
-ifeq ($(MCU_SERIES),stm32f1) # FIXME add F2 support
cSRCS_$(d) += dma.c
-endif
cSRCS_$(d) += exti.c
cSRCS_$(d) += flash.c
cSRCS_$(d) += gpio.c
diff --git a/libmaple/stm32f2/dma.c b/libmaple/stm32f2/dma.c
new file mode 100644
index 0000000..26e87b9
--- /dev/null
+++ b/libmaple/stm32f2/dma.c
@@ -0,0 +1,504 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2012 LeafLabs, LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
+
+/**
+ * @file libmaple/stm32f2/dma.c
+ * @author Marti Bolivar <mbolivar@leaflabs.com>
+ * @brief STM32F2 DMA support.
+ */
+
+#include <libmaple/dma.h>
+#include <libmaple/bitband.h>
+#include <libmaple/util.h>
+
+/* Hack to ensure inlining in dma_irq_handler() */
+#define DMA_GET_HANDLER(dev, tube) (dev->handlers[tube].handler)
+#include "dma_private.h"
+
+/*
+ * Devices
+ */
+
+static dma_dev dma1 = {
+ .regs = DMA1_BASE,
+ .clk_id = RCC_DMA1,
+ .handlers = {{ .handler = NULL, .irq_line = NVIC_DMA1_STREAM0 },
+ { .handler = NULL, .irq_line = NVIC_DMA1_STREAM1 },
+ { .handler = NULL, .irq_line = NVIC_DMA1_STREAM2 },
+ { .handler = NULL, .irq_line = NVIC_DMA1_STREAM3 },
+ { .handler = NULL, .irq_line = NVIC_DMA1_STREAM4 },
+ { .handler = NULL, .irq_line = NVIC_DMA1_STREAM5 },
+ { .handler = NULL, .irq_line = NVIC_DMA1_STREAM6 },
+ { .handler = NULL, .irq_line = NVIC_DMA1_STREAM7 }},
+};
+dma_dev *DMA1 = &dma1;
+
+static dma_dev dma2 = {
+ .regs = DMA2_BASE,
+ .clk_id = RCC_DMA2,
+ .handlers = {{ .handler = NULL, .irq_line = NVIC_DMA2_STREAM0 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_STREAM1 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_STREAM2 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_STREAM3 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_STREAM4 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_STREAM5 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_STREAM6 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_STREAM7 }},
+};
+dma_dev *DMA2 = &dma2;
+
+/*
+ * Helpers for dealing with dma_request_src's bit encoding (see the
+ * comments in the dma_request_src definition).
+ */
+
+/* rcc_clk_id of dma_dev which supports src. */
+static __always_inline rcc_clk_id src_clk_id(dma_request_src src) {
+ return (rcc_clk_id)(((uint32)src >> 3) & 0x3F);
+}
+
+/* Bit vector of streams supporting src (e.g., bit 0 set => DMA_S0 support). */
+static __always_inline uint32 src_stream_mask(dma_request_src src) {
+ return ((uint32)src >> 10) & 0xFF;
+}
+
+/* Channel corresponding to src. */
+static __always_inline dma_channel src_channel(dma_request_src src) {
+ return (dma_channel)(src & 0x7);
+}
+
+/*
+ * Routines
+ */
+
+/* For convenience */
+#define ASSERT_NOT_ENABLED(dev, tube) ASSERT(!dma_is_enabled(dev, tube))
+
+/* Helpers for dma_tube_cfg() */
+static int preconfig_check(dma_dev *dev, dma_tube tube, dma_tube_config *cfg);
+static int postconfig_check(dma_tube_reg_map *dummy, dma_tube_config *cfg);
+static int config_fifo(dma_tube_reg_map *dummy, dma_tube_config *cfg);
+static int config_src_dst(dma_tube_reg_map *dummy, dma_tube_config *cfg);
+static void copy_regs(dma_tube_reg_map *src, dma_tube_reg_map *dst);
+
+int dma_tube_cfg(dma_dev *dev, dma_tube tube, dma_tube_config *cfg) {
+ dma_tube_reg_map dummy_regs;
+ dma_tube_reg_map *tregs = dma_tube_regs(dev, tube);
+ int ret;
+
+ /* Initial error checking. */
+ ret = preconfig_check(dev, tube, cfg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* Disable `tube' as per RM0033. */
+ dma_disable(dev, tube);
+ dma_clear_isr_bits(dev, tube);
+
+ /* Don't write to tregs until we've decided `cfg' is really OK,
+ * so as not to make a half-formed mess if we have to error out. */
+ copy_regs(tregs, &dummy_regs);
+
+ /* Try to reconfigure `tube', bailing on error. */
+ ret = config_fifo(&dummy_regs, cfg);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = config_src_dst(&dummy_regs, cfg);
+ if (ret < 0) {
+ return ret;
+ }
+ dummy_regs.SNDTR = cfg->tube_nr_xfers;
+ ret = postconfig_check(&dummy_regs, cfg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* Ok, we're good. Commit to the new configuration. */
+ copy_regs(&dummy_regs, tregs);
+ return ret;
+}
+
+void dma_set_priority(dma_dev *dev, dma_stream stream, dma_priority priority) {
+ dma_tube_reg_map *tregs = dma_tube_regs(dev, stream);
+ uint32 scr;
+ ASSERT_NOT_ENABLED(dev, stream);
+ scr = tregs->SCR;
+ scr &= ~DMA_SCR_PL;
+ scr |= (priority << 16);
+ tregs->SCR = scr;
+}
+
+void dma_set_num_transfers(dma_dev *dev, dma_tube tube, uint16 num_transfers) {
+ dma_tube_reg_map *tregs = dma_tube_regs(dev, tube);
+ ASSERT_NOT_ENABLED(dev, tube);
+ tregs->SNDTR = num_transfers;
+}
+
+/**
+ * @brief Set memory 0 or memory 1 address.
+ *
+ * This is a general function for setting one of the two memory
+ * addresses available on the double-buffered STM32F2 DMA controllers.
+ *
+ * @param dev DMA device
+ * @param tube Tube on dev.
+ * @param n If 0, set memory 0 address. If 1, set memory 1 address.
+ * @param address Address to set
+ */
+void dma_set_mem_n_addr(dma_dev *dev, dma_tube tube, int n,
+ __io void *address) {
+ dma_tube_reg_map *tregs = dma_tube_regs(dev, tube);
+ uint32 addr = (uint32)address;
+
+ ASSERT_NOT_ENABLED(dev, tube);
+ if (n) {
+ tregs->SM1AR = addr;
+ } else {
+ tregs->SM0AR = addr;
+ }
+}
+
+void dma_set_per_addr(dma_dev *dev, dma_tube tube, __io void *address) {
+ dma_tube_reg_map *tregs = dma_tube_regs(dev, tube);
+ ASSERT_NOT_ENABLED(dev, tube);
+ tregs->SPAR = (uint32)address;
+}
+
+/**
+ * @brief Enable a stream's FIFO.
+ *
+ * You may only call this function when the stream is disabled.
+ *
+ * @param dev DMA device
+ * @param tube Stream whose FIFO to enable.
+ */
+void dma_enable_fifo(dma_dev *dev, dma_tube tube) {
+ ASSERT_NOT_ENABLED(dev, tube);
+ bb_peri_set_bit(&(dma_tube_regs(dev, tube)->SFCR), DMA_SFCR_DMDIS_BIT, 1);
+}
+
+/**
+ * @brief Disable a stream's FIFO.
+ *
+ * You may only call this function when the stream is disabled.
+ *
+ * @param dev DMA device
+ * @param tube Stream whose FIFO to disable.
+ */
+void dma_disable_fifo(dma_dev *dev, dma_tube tube) {
+ ASSERT_NOT_ENABLED(dev, tube);
+ bb_peri_set_bit(&(dma_tube_regs(dev, tube)->SFCR), DMA_SFCR_DMDIS_BIT, 0);
+}
+
+void dma_attach_interrupt(dma_dev *dev, dma_tube tube,
+ void (*handler)(void)) {
+ dev->handlers[tube].handler = handler;
+ nvic_irq_enable(dev->handlers[tube].irq_line);
+}
+
+void dma_detach_interrupt(dma_dev *dev, dma_tube tube) {
+ nvic_irq_disable(dev->handlers[tube].irq_line);
+ dev->handlers[tube].handler = NULL;
+}
+
+void dma_enable(dma_dev *dev, dma_tube tube) {
+ dma_tube_reg_map *tregs = dma_tube_regs(dev, tube);
+ bb_peri_set_bit(&tregs->SCR, DMA_SCR_EN_BIT, 1);
+}
+
+void dma_disable(dma_dev *dev, dma_tube tube) {
+ dma_tube_reg_map *tregs = dma_tube_regs(dev, tube);
+ bb_peri_set_bit(&tregs->SCR, DMA_SCR_EN_BIT, 0);
+ /* The stream might not get disabled immediately, so wait. */
+ while (tregs->SCR & DMA_SCR_EN)
+ ;
+}
+
+dma_irq_cause dma_get_irq_cause(dma_dev *dev, dma_tube tube) {
+ /* TODO: does it still make sense to have this function? We should
+ * probably just be returning the ISR bits, with some defines to
+ * pull the flags out. The lack of masked status bits is an
+ * annoyance that would require documentation to solve, though. */
+ uint8 status_bits = dma_get_isr_bits(dev, tube);
+ dma_clear_isr_bits(dev, tube);
+ ASSERT(status_bits); /* Or something's very wrong */
+ /* Don't change the order of these if statements. */
+ if (status_bits & 0x0) {
+ return DMA_TRANSFER_FIFO_ERROR;
+ } else if (status_bits & 0x4) {
+ return DMA_TRANSFER_DME_ERROR;
+ } else if (status_bits & 0x8) {
+ return DMA_TRANSFER_ERROR;
+ } else if (status_bits & 0x20) {
+ return DMA_TRANSFER_COMPLETE;
+ } else if (status_bits & 0x10) {
+ return DMA_TRANSFER_HALF_COMPLETE;
+ }
+
+ /* Something's wrong; one of those bits should have been set. Fail
+ * an assert, and mimic the error behavior in case of a high debug
+ * level. */
+ ASSERT(0);
+ dma_disable(dev, tube);
+ return DMA_TRANSFER_ERROR;
+}
+
+/*
+ * IRQ handlers
+ */
+
+void __irq_dma1_stream0(void) {
+ dma_irq_handler(DMA1, DMA_S0);
+}
+
+void __irq_dma1_stream1(void) {
+ dma_irq_handler(DMA1, DMA_S1);
+}
+
+void __irq_dma1_stream2(void) {
+ dma_irq_handler(DMA1, DMA_S2);
+}
+
+void __irq_dma1_stream3(void) {
+ dma_irq_handler(DMA1, DMA_S3);
+}
+
+void __irq_dma1_stream4(void) {
+ dma_irq_handler(DMA1, DMA_S4);
+}
+
+void __irq_dma1_stream5(void) {
+ dma_irq_handler(DMA1, DMA_S5);
+}
+
+void __irq_dma1_stream6(void) {
+ dma_irq_handler(DMA1, DMA_S6);
+}
+
+void __irq_dma1_stream7(void) {
+ dma_irq_handler(DMA1, DMA_S7);
+}
+
+void __irq_dma2_stream0(void) {
+ dma_irq_handler(DMA2, DMA_S0);
+}
+
+void __irq_dma2_stream1(void) {
+ dma_irq_handler(DMA2, DMA_S1);
+}
+
+void __irq_dma2_stream2(void) {
+ dma_irq_handler(DMA2, DMA_S2);
+}
+
+void __irq_dma2_stream3(void) {
+ dma_irq_handler(DMA2, DMA_S3);
+}
+
+void __irq_dma2_stream4(void) {
+ dma_irq_handler(DMA2, DMA_S4);
+}
+
+void __irq_dma2_stream5(void) {
+ dma_irq_handler(DMA2, DMA_S5);
+}
+
+void __irq_dma2_stream6(void) {
+ dma_irq_handler(DMA2, DMA_S6);
+}
+
+void __irq_dma2_stream7(void) {
+ dma_irq_handler(DMA2, DMA_S7);
+}
+
+/*
+ * Auxiliary routines for dma_tube_cfg()
+ */
+
+/* Is addr acceptable for use as DMA src/dst? */
+static int cfg_mem_ok(__io void *addr) {
+ enum dma_atype atype = _dma_addr_type(addr);
+ return atype == DMA_ATYPE_MEM || atype == DMA_ATYPE_PER;
+}
+
+/* Is src -> dst a reasonable combination of [MEM,PER] -> [MEM,PER]? */
+static int cfg_dir_ok(dma_dev *dev, __io void *src, __io void *dst) {
+ switch (_dma_addr_type(dst)) {
+ case DMA_ATYPE_MEM:
+ /* Only DMA2 can do memory-to-memory */
+ return ((_dma_addr_type(src) == DMA_ATYPE_PER) ||
+ (dev->clk_id == RCC_DMA2));
+ case DMA_ATYPE_PER:
+ /* Peripheral-to-peripheral is illegal */
+ return _dma_addr_type(src) == DMA_ATYPE_PER;
+ default: /* Can't happen */
+ ASSERT(0);
+ return 0;
+ }
+}
+
+/* Initial sanity check for dma_tube_cfg() */
+static int preconfig_check(dma_dev *dev, dma_tube tube,
+ dma_tube_config *cfg) {
+ if (!(src_stream_mask(cfg->tube_req_src) & (1U << tube))) {
+ /* ->tube_req_src not supported by stream */
+ return -DMA_TUBE_CFG_EREQ;
+ }
+ if (cfg->tube_nr_xfers > 65535) {
+ /* That's too many. */
+ return -DMA_TUBE_CFG_ENDATA;
+ }
+ if (src_clk_id(cfg->tube_req_src) != dev->clk_id) {
+ /* ->tube_req_src not supported by dev */
+ return -DMA_TUBE_CFG_EDEV;
+ }
+ if (!cfg_mem_ok(cfg->tube_src)) {
+ return -DMA_TUBE_CFG_ESRC;
+ }
+ if (!cfg_mem_ok(cfg->tube_dst)) {
+ return -DMA_TUBE_CFG_EDST;
+ }
+ if (!cfg_dir_ok(dev, cfg->tube_src, cfg->tube_dst)) {
+ return -DMA_TUBE_CFG_EDIR;
+ }
+ return DMA_TUBE_CFG_SUCCESS;
+}
+
+static int config_fifo(dma_tube_reg_map *dummy, dma_tube_config *cfg) {
+ /* TODO: FIFO configuration based on cfg->target_data */
+ uint32 sfcr = dummy->SFCR;
+ sfcr &= ~DMA_SFCR_FEIE;
+ sfcr |= (cfg->tube_flags & DMA_CFG_FIFO_ERR_IE) ? DMA_SFCR_FEIE : 0;
+ dummy->SFCR = sfcr;
+ return DMA_TUBE_CFG_SUCCESS;
+}
+
+/* Helper for configuring (DMA_SxCR) */
+#define BITS_WE_CARE_ABOUT \
+ (DMA_SCR_CHSEL | DMA_SCR_MBURST | DMA_SCR_PBURST | DMA_SCR_PINCOS | \
+ DMA_SCR_MINC | DMA_SCR_PINC | DMA_SCR_CIRC | DMA_SCR_DIR | \
+ DMA_SCR_PFCTRL | DMA_SCR_TCIE | DMA_SCR_HTIE | DMA_SCR_TEIE | \
+ DMA_SCR_DMEIE)
+static inline void config_scr(dma_tube_reg_map *dummy, dma_tube_config *cfg,
+ unsigned src_shift, uint32 src_inc,
+ unsigned dst_shift, uint32 dst_inc,
+ uint32 dir) {
+ /* These would go here if we supported them: MBURST, PBURST,
+ * PINCOS, PFCTRL. We explicitly choose low priority, and double
+ * buffering belongs elsewhere, I think. [mbolivar] */
+ uint32 flags = cfg->tube_flags & BITS_WE_CARE_ABOUT;
+ uint32 scr = dummy->SCR;
+ scr &= ~(BITS_WE_CARE_ABOUT | DMA_SCR_PL);
+ scr |= (/* CHSEL */
+ (src_channel(cfg->tube_req_src) << 25) |
+ /* MSIZE/PSIZE */
+ (cfg->tube_src_size << src_shift) |
+ (cfg->tube_dst_size << dst_shift) |
+ /* MINC/PINC */
+ ((cfg->tube_flags & DMA_CFG_SRC_INC) ? src_inc : 0) |
+ ((cfg->tube_flags & DMA_CFG_DST_INC) ? dst_inc : 0) |
+ /* DIR */
+ dir |
+ /* Other flags carried by cfg->tube_flags */
+ flags);
+ dummy->SCR = scr;
+}
+#undef BITS_WE_CARE_ABOUT
+
+/* Helper for when cfg->tube_dst is memory */
+static int config_to_mem(dma_tube_reg_map *dummy, dma_tube_config *cfg) {
+ uint32 dir = (_dma_addr_type(cfg->tube_src) == DMA_ATYPE_MEM ?
+ DMA_SCR_DIR_MEM_TO_MEM : DMA_SCR_DIR_PER_TO_MEM);
+
+ if ((dir == DMA_SCR_DIR_MEM_TO_MEM) && (cfg->tube_flags & DMA_CFG_CIRC)) {
+ return -DMA_TUBE_CFG_ECFG; /* Can't do DMA_CFG_CIRC and mem->mem. */
+ }
+
+ config_scr(dummy, cfg, 11, DMA_SCR_PINC, 13, DMA_SCR_MINC, dir);
+ dummy->SPAR = (uint32)cfg->tube_src;
+ dummy->SM0AR = (uint32)cfg->tube_dst;
+ return DMA_TUBE_CFG_SUCCESS;
+}
+
+/* Helper for when cfg->tube_src is peripheral */
+static int config_to_per(dma_tube_reg_map *dummy, dma_tube_config *cfg) {
+ config_scr(dummy, cfg, 13, DMA_SCR_MINC, 11, DMA_SCR_PINC,
+ DMA_SCR_DIR_MEM_TO_PER);
+ dummy->SM0AR = (uint32)cfg->tube_src;
+ dummy->SPAR = (uint32)cfg->tube_dst;
+ return DMA_TUBE_CFG_SUCCESS;
+}
+
+/* Configures SCR, SPAR, SM0AR, and checks that the result is OK. */
+static int config_src_dst(dma_tube_reg_map *dummy, dma_tube_config *cfg) {
+ switch (_dma_addr_type(cfg->tube_dst)) {
+ case DMA_ATYPE_MEM:
+ return config_to_mem(dummy, cfg);
+ case DMA_ATYPE_PER:
+ return config_to_per(dummy, cfg);
+ case DMA_ATYPE_OTHER:
+ default: /* shut up, GCC */
+ /* Can't happen */
+ ASSERT(0);
+ return -DMA_TUBE_CFG_ECFG;
+ }
+}
+
+/* Final checks we can only perform when fully configured */
+static int postconfig_check(dma_tube_reg_map *dummy, dma_tube_config *cfg) {
+ /* TODO add dma_get_[mem,per]_size() and use them here */
+ /* msize and psize are in bytes here: */
+ uint32 scr = dummy->SCR;
+ uint32 msize = 1U << ((scr >> 13) & 0x3);
+ uint32 psize = 1U << ((scr >> 11) & 0x3);
+
+ /* Ensure NDT will work with PSIZE/MSIZE.
+ *
+ * RM0033 specifies that PSIZE, MSIZE, and NDT must be such that
+ * the last transfer completes; i.e. that if PSIZE < MSIZE, then
+ * NDT is a multiple of MSIZE/PSIZE. See e.g. Table 27. */
+ if ((psize < msize) && (cfg->tube_nr_xfers % (msize / psize))) {
+ return -DMA_TUBE_CFG_ENDATA;
+ }
+
+ /* Direct mode is only possible if MSIZE == PSIZE. */
+ if ((msize != psize) && !(dummy->SFCR & DMA_SFCR_DMDIS)) {
+ return -DMA_TUBE_CFG_ESIZE;
+ }
+
+ return DMA_TUBE_CFG_SUCCESS;
+}
+
+/* Convenience for dealing with dummy registers */
+static void copy_regs(dma_tube_reg_map *src, dma_tube_reg_map *dst) {
+ dst->SCR = src->SCR;
+ dst->SNDTR = src->SNDTR;
+ dst->SPAR = src->SPAR;
+ dst->SM0AR = src->SM0AR;
+ dst->SFCR = src->SFCR;
+}
diff --git a/libmaple/stm32f2/include/series/dma.h b/libmaple/stm32f2/include/series/dma.h
new file mode 100644
index 0000000..43bd1a2
--- /dev/null
+++ b/libmaple/stm32f2/include/series/dma.h
@@ -0,0 +1,810 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2012 LeafLabs, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
+
+/**
+ * @file libmaple/stm32f2/include/series/dma.h
+ * @author Marti Bolivar <mbolivar@leaflabs.com>
+ * @brief STM32F2 DMA series header
+ */
+
+#ifndef _LIBMAPLE_STM32F2_DMA_H_
+#define _LIBMAPLE_STM32F2_DMA_H_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#include <libmaple/dma_common.h>
+#include <libmaple/libmaple_types.h>
+
+/*
+ * Register map and base pointers
+ */
+
+/**
+ * @brief STM32F2 DMA register map type.
+ */
+typedef struct dma_reg_map {
+ /* Isn't it nice how on F1, it's CCR1, but on F2, it's S1CR? */
+
+ /* Global DMA registers */
+ __io uint32 LISR; /**< Low interrupt status register */
+ __io uint32 HISR; /**< High interrupt status register */
+ __io uint32 LIFCR; /**< Low interrupt flag clear register */
+ __io uint32 HIFCR; /**< High interrupt flag clear register */
+ /* Stream 0 registers */
+ __io uint32 S0CR; /**< Stream 0 control register */
+ __io uint32 S0NDTR; /**< Stream 0 number of data register */
+ __io uint32 S0PAR; /**< Stream 0 peripheral address register */
+ __io uint32 S0M0AR; /**< Stream 0 memory 0 address register */
+ __io uint32 S0M1AR; /**< Stream 0 memory 1 address register */
+ __io uint32 S0FCR; /**< Stream 0 FIFO control register */
+ /* Stream 1 registers */
+ __io uint32 S1CR; /**< Stream 1 control register */
+ __io uint32 S1NDTR; /**< Stream 1 number of data register */
+ __io uint32 S1PAR; /**< Stream 1 peripheral address register */
+ __io uint32 S1M0AR; /**< Stream 1 memory 0 address register */
+ __io uint32 S1M1AR; /**< Stream 1 memory 1 address register */
+ __io uint32 S1FCR; /**< Stream 1 FIFO control register */
+ /* Stream 2 registers */
+ __io uint32 S2CR; /**< Stream 2 control register */
+ __io uint32 S2NDTR; /**< Stream 2 number of data register */
+ __io uint32 S2PAR; /**< Stream 2 peripheral address register */
+ __io uint32 S2M0AR; /**< Stream 2 memory 0 address register */
+ __io uint32 S2M1AR; /**< Stream 2 memory 1 address register */
+ __io uint32 S2FCR; /**< Stream 2 FIFO control register */
+ /* Stream 3 registers */
+ __io uint32 S3CR; /**< Stream 3 control register */
+ __io uint32 S3NDTR; /**< Stream 3 number of data register */
+ __io uint32 S3PAR; /**< Stream 3 peripheral address register */
+ __io uint32 S3M0AR; /**< Stream 3 memory 0 address register */
+ __io uint32 S3M1AR; /**< Stream 3 memory 1 address register */
+ __io uint32 S3FCR; /**< Stream 3 FIFO control register */
+ /* Stream 4 registers */
+ __io uint32 S4CR; /**< Stream 4 control register */
+ __io uint32 S4NDTR; /**< Stream 4 number of data register */
+ __io uint32 S4PAR; /**< Stream 4 peripheral address register */
+ __io uint32 S4M0AR; /**< Stream 4 memory 0 address register */
+ __io uint32 S4M1AR; /**< Stream 4 memory 1 address register */
+ __io uint32 S4FCR; /**< Stream 4 FIFO control register */
+ /* Stream 5 registers */
+ __io uint32 S5CR; /**< Stream 5 control register */
+ __io uint32 S5NDTR; /**< Stream 5 number of data register */
+ __io uint32 S5PAR; /**< Stream 5 peripheral address register */
+ __io uint32 S5M0AR; /**< Stream 5 memory 0 address register */
+ __io uint32 S5M1AR; /**< Stream 5 memory 1 address register */
+ __io uint32 S5FCR; /**< Stream 5 FIFO control register */
+ /* Stream 6 registers */
+ __io uint32 S6CR; /**< Stream 6 control register */
+ __io uint32 S6NDTR; /**< Stream 6 number of data register */
+ __io uint32 S6PAR; /**< Stream 6 peripheral address register */
+ __io uint32 S6M0AR; /**< Stream 6 memory 0 address register */
+ __io uint32 S6M1AR; /**< Stream 6 memory 1 address register */
+ __io uint32 S6FCR; /**< Stream 6 FIFO control register */
+ /* Stream 7 registers */
+ __io uint32 S7CR; /**< Stream 7 control register */
+ __io uint32 S7NDTR; /**< Stream 7 number of data register */
+ __io uint32 S7PAR; /**< Stream 7 peripheral address register */
+ __io uint32 S7M0AR; /**< Stream 7 memory 0 address register */
+ __io uint32 S7M1AR; /**< Stream 7 memory 1 address register */
+ __io uint32 S7FCR; /**< Stream 7 FIFO control register */
+} dma_reg_map;
+
+/** DMA controller 1 register map base pointer */
+#define DMA1_BASE ((struct dma_reg_map*)0x40026000)
+/** DMA controller 2 register map base pointer */
+#define DMA2_BASE ((struct dma_reg_map*)0x40026400)
+
+/**
+ * @brief STM32F2 DMA stream (i.e. tube) register map type.
+ * Provides access to an individual stream's registers.
+ * @see dma_tube_regs()
+ */
+typedef struct dma_tube_reg_map {
+ __io uint32 SCR; /**< Stream configuration register */
+ __io uint32 SNDTR; /**< Stream number of data register */
+ __io uint32 SPAR; /**< Stream peripheral address register */
+ __io uint32 SM0AR; /**< Stream memory 0 address register */
+ __io uint32 SM1AR; /**< Stream memory 1 address register */
+ __io uint32 SFCR; /**< Stream FIFO control register */
+} dma_tube_reg_map;
+
+/** DMA1 stream 0 register map base pointer */
+#define DMA1S0_BASE ((struct dma_tube_reg_map*)0x40026010)
+/** DMA1 stream 1 register map base pointer */
+#define DMA1S1_BASE ((struct dma_tube_reg_map*)0x40026028)
+/** DMA1 stream 2 register map base pointer */
+#define DMA1S2_BASE ((struct dma_tube_reg_map*)0x40026040)
+/** DMA1 stream 3 register map base pointer */
+#define DMA1S3_BASE ((struct dma_tube_reg_map*)0x40026058)
+/** DMA1 stream 4 register map base pointer */
+#define DMA1S4_BASE ((struct dma_tube_reg_map*)0x40026070)
+/** DMA1 stream 5 register map base pointer */
+#define DMA1S5_BASE ((struct dma_tube_reg_map*)0x40026088)
+/** DMA1 stream 6 register map base pointer */
+#define DMA1S6_BASE ((struct dma_tube_reg_map*)0x400260A0)
+/** DMA1 stream 7 register map base pointer */
+#define DMA1S7_BASE ((struct dma_tube_reg_map*)0x400260B8)
+
+/** DMA2 stream 0 register map base pointer */
+#define DMA2S0_BASE ((struct dma_tube_reg_map*)0x40026410)
+/** DMA2 stream 1 register map base pointer */
+#define DMA2S1_BASE ((struct dma_tube_reg_map*)0x40026028)
+/** DMA2 stream 2 register map base pointer */
+#define DMA2S2_BASE ((struct dma_tube_reg_map*)0x40026040)
+/** DMA2 stream 3 register map base pointer */
+#define DMA2S3_BASE ((struct dma_tube_reg_map*)0x40026058)
+/** DMA2 stream 4 register map base pointer */
+#define DMA2S4_BASE ((struct dma_tube_reg_map*)0x40026070)
+/** DMA2 stream 5 register map base pointer */
+#define DMA2S5_BASE ((struct dma_tube_reg_map*)0x40026088)
+/** DMA2 stream 6 register map base pointer */
+#define DMA2S6_BASE ((struct dma_tube_reg_map*)0x400260A0)
+/** DMA2 stream 7 register map base pointer */
+#define DMA2S7_BASE ((struct dma_tube_reg_map*)0x400260B8)
+
+/*
+ * Register bit definitions
+ */
+
+/* Low interrupt status register */
+
+#define DMA_LISR_TCIF3_BIT 27
+#define DMA_LISR_HTIF3_BIT 26
+#define DMA_LISR_TEIF3_BIT 25
+#define DMA_LISR_DMEIF3_BIT 24
+#define DMA_LISR_FEIF3_BIT 22
+#define DMA_LISR_TCIF2_BIT 21
+#define DMA_LISR_HTIF2_BIT 20
+#define DMA_LISR_TEIF2_BIT 19
+#define DMA_LISR_DMEIF2_BIT 18
+#define DMA_LISR_FEIF2_BIT 16
+#define DMA_LISR_TCIF1_BIT 11
+#define DMA_LISR_HTIF1_BIT 10
+#define DMA_LISR_TEIF1_BIT 9
+#define DMA_LISR_DMEIF1_BIT 8
+#define DMA_LISR_FEIF1_BIT 6
+#define DMA_LISR_TCIF0_BIT 5
+#define DMA_LISR_HTIF0_BIT 4
+#define DMA_LISR_TEIF0_BIT 3
+#define DMA_LISR_DMEIF0_BIT 2
+#define DMA_LISR_FEIF0_BIT 0
+
+#define DMA_LISR_TCIF3 (1U << DMA_LISR_TCIF3_BIT)
+#define DMA_LISR_HTIF3 (1U << DMA_LISR_HTIF3_BIT)
+#define DMA_LISR_TEIF3 (1U << DMA_LISR_TEIF3_BIT)
+#define DMA_LISR_DMEIF3 (1U << DMA_LISR_DMEIF3_BIT)
+#define DMA_LISR_FEIF3 (1U << DMA_LISR_FEIF3_BIT)
+#define DMA_LISR_TCIF2 (1U << DMA_LISR_TCIF2_BIT)
+#define DMA_LISR_HTIF2 (1U << DMA_LISR_HTIF2_BIT)
+#define DMA_LISR_TEIF2 (1U << DMA_LISR_TEIF2_BIT)
+#define DMA_LISR_DMEIF2 (1U << DMA_LISR_DMEIF2_BIT)
+#define DMA_LISR_FEIF2 (1U << DMA_LISR_FEIF2_BIT)
+#define DMA_LISR_TCIF1 (1U << DMA_LISR_TCIF1_BIT)
+#define DMA_LISR_HTIF1 (1U << DMA_LISR_HTIF1_BIT)
+#define DMA_LISR_TEIF1 (1U << DMA_LISR_TEIF1_BIT)
+#define DMA_LISR_DMEIF1 (1U << DMA_LISR_DMEIF1_BIT)
+#define DMA_LISR_FEIF1 (1U << DMA_LISR_FEIF1_BIT)
+#define DMA_LISR_TCIF0 (1U << DMA_LISR_TCIF0_BIT)
+#define DMA_LISR_HTIF0 (1U << DMA_LISR_HTIF0_BIT)
+#define DMA_LISR_TEIF0 (1U << DMA_LISR_TEIF0_BIT)
+#define DMA_LISR_DMEIF0 (1U << DMA_LISR_DMEIF0_BIT)
+#define DMA_LISR_FEIF0 (1U << DMA_LISR_FEIF0_BIT)
+
+/* High interrupt status register */
+
+#define DMA_HISR_TCIF7_BIT 27
+#define DMA_HISR_HTIF7_BIT 26
+#define DMA_HISR_TEIF7_BIT 25
+#define DMA_HISR_DMEIF7_BIT 24
+#define DMA_HISR_FEIF7_BIT 22
+#define DMA_HISR_TCIF6_BIT 21
+#define DMA_HISR_HTIF6_BIT 20
+#define DMA_HISR_TEIF6_BIT 19
+#define DMA_HISR_DMEIF6_BIT 18
+#define DMA_HISR_FEIF6_BIT 16
+#define DMA_HISR_TCIF5_BIT 11
+#define DMA_HISR_HTIF5_BIT 10
+#define DMA_HISR_TEIF5_BIT 9
+#define DMA_HISR_DMEIF5_BIT 8
+#define DMA_HISR_FEIF5_BIT 6
+#define DMA_HISR_TCIF4_BIT 5
+#define DMA_HISR_HTIF4_BIT 4
+#define DMA_HISR_TEIF4_BIT 3
+#define DMA_HISR_DMEIF4_BIT 2
+#define DMA_HISR_FEIF4_BIT 0
+
+#define DMA_HISR_TCIF7 (1U << DMA_HISR_TCIF7_BIT)
+#define DMA_HISR_HTIF7 (1U << DMA_HISR_HTIF7_BIT)
+#define DMA_HISR_TEIF7 (1U << DMA_HISR_TEIF7_BIT)
+#define DMA_HISR_DMEIF7 (1U << DMA_HISR_DMEIF7_BIT)
+#define DMA_HISR_FEIF7 (1U << DMA_HISR_FEIF7_BIT)
+#define DMA_HISR_TCIF6 (1U << DMA_HISR_TCIF6_BIT)
+#define DMA_HISR_HTIF6 (1U << DMA_HISR_HTIF6_BIT)
+#define DMA_HISR_TEIF6 (1U << DMA_HISR_TEIF6_BIT)
+#define DMA_HISR_DMEIF6 (1U << DMA_HISR_DMEIF6_BIT)
+#define DMA_HISR_FEIF6 (1U << DMA_HISR_FEIF6_BIT)
+#define DMA_HISR_TCIF5 (1U << DMA_HISR_TCIF5_BIT)
+#define DMA_HISR_HTIF5 (1U << DMA_HISR_HTIF5_BIT)
+#define DMA_HISR_TEIF5 (1U << DMA_HISR_TEIF5_BIT)
+#define DMA_HISR_DMEIF5 (1U << DMA_HISR_DMEIF5_BIT)
+#define DMA_HISR_FEIF5 (1U << DMA_HISR_FEIF5_BIT)
+#define DMA_HISR_TCIF4 (1U << DMA_HISR_TCIF4_BIT)
+#define DMA_HISR_HTIF4 (1U << DMA_HISR_HTIF4_BIT)
+#define DMA_HISR_TEIF4 (1U << DMA_HISR_TEIF4_BIT)
+#define DMA_HISR_DMEIF4 (1U << DMA_HISR_DMEIF4_BIT)
+#define DMA_HISR_FEIF4 (1U << DMA_HISR_FEIF4_BIT)
+
+/* Low interrupt flag clear register */
+
+#define DMA_LIFCR_CTCIF3_BIT 27
+#define DMA_LIFCR_CHTIF3_BIT 26
+#define DMA_LIFCR_CTEIF3_BIT 25
+#define DMA_LIFCR_CDMEIF3_BIT 24
+#define DMA_LIFCR_CFEIF3_BIT 22
+#define DMA_LIFCR_CTCIF2_BIT 21
+#define DMA_LIFCR_CHTIF2_BIT 20
+#define DMA_LIFCR_CTEIF2_BIT 19
+#define DMA_LIFCR_CDMEIF2_BIT 18
+#define DMA_LIFCR_CFEIF2_BIT 16
+#define DMA_LIFCR_CTCIF1_BIT 11
+#define DMA_LIFCR_CHTIF1_BIT 10
+#define DMA_LIFCR_CTEIF1_BIT 9
+#define DMA_LIFCR_CDMEIF1_BIT 8
+#define DMA_LIFCR_CFEIF1_BIT 6
+#define DMA_LIFCR_CTCIF0_BIT 5
+#define DMA_LIFCR_CHTIF0_BIT 4
+#define DMA_LIFCR_CTEIF0_BIT 3
+#define DMA_LIFCR_CDMEIF0_BIT 2
+#define DMA_LIFCR_CFEIF0_BIT 0
+
+#define DMA_LIFCR_CTCIF3 (1U << DMA_LIFCR_CTCIF3_BIT)
+#define DMA_LIFCR_CHTIF3 (1U << DMA_LIFCR_CHTIF3_BIT)
+#define DMA_LIFCR_CTEIF3 (1U << DMA_LIFCR_CTEIF3_BIT)
+#define DMA_LIFCR_CDMEIF3 (1U << DMA_LIFCR_CDMEIF3_BIT)
+#define DMA_LIFCR_CFEIF3 (1U << DMA_LIFCR_CFEIF3_BIT)
+#define DMA_LIFCR_CTCIF2 (1U << DMA_LIFCR_CTCIF2_BIT)
+#define DMA_LIFCR_CHTIF2 (1U << DMA_LIFCR_CHTIF2_BIT)
+#define DMA_LIFCR_CTEIF2 (1U << DMA_LIFCR_CTEIF2_BIT)
+#define DMA_LIFCR_CDMEIF2 (1U << DMA_LIFCR_CDMEIF2_BIT)
+#define DMA_LIFCR_CFEIF2 (1U << DMA_LIFCR_CFEIF2_BIT)
+#define DMA_LIFCR_CTCIF1 (1U << DMA_LIFCR_CTCIF1_BIT)
+#define DMA_LIFCR_CHTIF1 (1U << DMA_LIFCR_CHTIF1_BIT)
+#define DMA_LIFCR_CTEIF1 (1U << DMA_LIFCR_CTEIF1_BIT)
+#define DMA_LIFCR_CDMEIF1 (1U << DMA_LIFCR_CDMEIF1_BIT)
+#define DMA_LIFCR_CFEIF1 (1U << DMA_LIFCR_CFEIF1_BIT)
+#define DMA_LIFCR_CTCIF0 (1U << DMA_LIFCR_CTCIF0_BIT)
+#define DMA_LIFCR_CHTIF0 (1U << DMA_LIFCR_CHTIF0_BIT)
+#define DMA_LIFCR_CTEIF0 (1U << DMA_LIFCR_CTEIF0_BIT)
+#define DMA_LIFCR_CDMEIF0 (1U << DMA_LIFCR_CDMEIF0_BIT)
+#define DMA_LIFCR_CFEIF0 (1U << DMA_LIFCR_CFEIF0_BIT)
+
+/* High interrupt flag clear regsister */
+
+#define DMA_HIFCR_CTCIF7_BIT 27
+#define DMA_HIFCR_CHTIF7_BIT 26
+#define DMA_HIFCR_CTEIF7_BIT 25
+#define DMA_HIFCR_CDMEIF7_BIT 24
+#define DMA_HIFCR_CFEIF7_BIT 22
+#define DMA_HIFCR_CTCIF6_BIT 21
+#define DMA_HIFCR_CHTIF6_BIT 20
+#define DMA_HIFCR_CTEIF6_BIT 19
+#define DMA_HIFCR_CDMEIF6_BIT 18
+#define DMA_HIFCR_CFEIF6_BIT 16
+#define DMA_HIFCR_CTCIF5_BIT 11
+#define DMA_HIFCR_CHTIF5_BIT 10
+#define DMA_HIFCR_CTEIF5_BIT 9
+#define DMA_HIFCR_CDMEIF5_BIT 8
+#define DMA_HIFCR_CFEIF5_BIT 6
+#define DMA_HIFCR_CTCIF4_BIT 5
+#define DMA_HIFCR_CHTIF4_BIT 4
+#define DMA_HIFCR_CTEIF4_BIT 3
+#define DMA_HIFCR_CDMEIF4_BIT 2
+#define DMA_HIFCR_CFEIF4_BIT 0
+
+#define DMA_HIFCR_CTCIF7 (1U << DMA_HIFCR_CTCIF7_BIT)
+#define DMA_HIFCR_CHTIF7 (1U << DMA_HIFCR_CHTIF7_BIT)
+#define DMA_HIFCR_CTEIF7 (1U << DMA_HIFCR_CTEIF7_BIT)
+#define DMA_HIFCR_CDMEIF7 (1U << DMA_HIFCR_CDMEIF7_BIT)
+#define DMA_HIFCR_CFEIF7 (1U << DMA_HIFCR_CFEIF7_BIT)
+#define DMA_HIFCR_CTCIF6 (1U << DMA_HIFCR_CTCIF6_BIT)
+#define DMA_HIFCR_CHTIF6 (1U << DMA_HIFCR_CHTIF6_BIT)
+#define DMA_HIFCR_CTEIF6 (1U << DMA_HIFCR_CTEIF6_BIT)
+#define DMA_HIFCR_CDMEIF6 (1U << DMA_HIFCR_CDMEIF6_BIT)
+#define DMA_HIFCR_CFEIF6 (1U << DMA_HIFCR_CFEIF6_BIT)
+#define DMA_HIFCR_CTCIF5 (1U << DMA_HIFCR_CTCIF5_BIT)
+#define DMA_HIFCR_CHTIF5 (1U << DMA_HIFCR_CHTIF5_BIT)
+#define DMA_HIFCR_CTEIF5 (1U << DMA_HIFCR_CTEIF5_BIT)
+#define DMA_HIFCR_CDMEIF5 (1U << DMA_HIFCR_CDMEIF5_BIT)
+#define DMA_HIFCR_CFEIF5 (1U << DMA_HIFCR_CFEIF5_BIT)
+#define DMA_HIFCR_CTCIF4 (1U << DMA_HIFCR_CTCIF4_BIT)
+#define DMA_HIFCR_CHTIF4 (1U << DMA_HIFCR_CHTIF4_BIT)
+#define DMA_HIFCR_CTEIF4 (1U << DMA_HIFCR_CTEIF4_BIT)
+#define DMA_HIFCR_CDMEIF4 (1U << DMA_HIFCR_CDMEIF4_BIT)
+#define DMA_HIFCR_CFEIF4 (1U << DMA_HIFCR_CFEIF4_BIT)
+
+/* Stream configuration register */
+
+#define DMA_SCR_CT_BIT 19
+#define DMA_SCR_DBM_BIT 18
+#define DMA_SCR_PINCOS_BIT 15
+#define DMA_SCR_MINC_BIT 10
+#define DMA_SCR_PINC_BIT 9
+#define DMA_SCR_CIRC_BIT 8
+#define DMA_SCR_PFCTRL_BIT 5
+#define DMA_SCR_TCIE_BIT 4
+#define DMA_SCR_HTIE_BIT 3
+#define DMA_SCR_TEIE_BIT 2
+#define DMA_SCR_DMEIE_BIT 1
+#define DMA_SCR_EN_BIT 0
+
+#define DMA_SCR_CHSEL (0x7 << 25)
+#define DMA_SCR_CHSEL_CH_0 (0x0 << 25)
+#define DMA_SCR_CHSEL_CH_1 (0x1 << 25)
+#define DMA_SCR_CHSEL_CH_2 (0x2 << 25)
+#define DMA_SCR_CHSEL_CH_3 (0x3 << 25)
+#define DMA_SCR_CHSEL_CH_4 (0x4 << 25)
+#define DMA_SCR_CHSEL_CH_5 (0x5 << 25)
+#define DMA_SCR_CHSEL_CH_6 (0x6 << 25)
+#define DMA_SCR_CHSEL_CH_7 (0x7 << 25)
+#define DMA_SCR_MBURST (0x3 << 23)
+#define DMA_SCR_MBURST_SINGLE (0x0 << 23)
+#define DMA_SCR_MBURST_INCR4 (0x1 << 23)
+#define DMA_SCR_MBURST_INCR8 (0x2 << 23)
+#define DMA_SCR_MBURST_INCR16 (0x3 << 23)
+#define DMA_SCR_PBURST (0x3 << 21)
+#define DMA_SCR_PBURST_SINGLE (0x0 << 21)
+#define DMA_SCR_PBURST_INCR4 (0x1 << 21)
+#define DMA_SCR_PBURST_INCR8 (0x2 << 21)
+#define DMA_SCR_PBURST_INCR16 (0x3 << 21)
+#define DMA_SCR_CT (1U << DMA_SCR_CT_BIT)
+#define DMA_SCR_DBM (1U << DMA_SCR_DBM_BIT)
+#define DMA_SCR_PL (0x3 << 16)
+#define DMA_SCR_PL_LOW (0x0 << 16)
+#define DMA_SCR_PL_MEDIUM (0x1 << 16)
+#define DMA_SCR_PL_HIGH (0x2 << 16)
+#define DMA_SCR_VERY_HIGH (0x3 << 16)
+#define DMA_SCR_PINCOS (1U << DMA_SCR_PINCOS_BIT)
+#define DMA_SCR_MSIZE (0x3 << 13)
+#define DMA_SCR_MSIZE_8BITS (0x0 << 13)
+#define DMA_SCR_MSIZE_16BITS (0x1 << 13)
+#define DMA_SCR_MSIZE_32BITS (0x2 << 13)
+#define DMA_SCR_PSIZE (0x3 << 11)
+#define DMA_SCR_PSIZE_8BITS (0x0 << 11)
+#define DMA_SCR_PSIZE_16BITS (0x1 << 11)
+#define DMA_SCR_PSIZE_32BITS (0x2 << 11)
+#define DMA_SCR_MINC (1U << DMA_SCR_MINC_BIT)
+#define DMA_SCR_PINC (1U << DMA_SCR_PINC_BIT)
+#define DMA_SCR_CIRC (1U << DMA_SCR_CIRC_BIT)
+#define DMA_SCR_DIR (0x3 << 6)
+#define DMA_SCR_DIR_PER_TO_MEM (0x0 << 6)
+#define DMA_SCR_DIR_MEM_TO_PER (0x1 << 6)
+#define DMA_SCR_DIR_MEM_TO_MEM (0x2 << 6)
+#define DMA_SCR_PFCTRL (1U << DMA_SCR_PFCTRL_BIT)
+#define DMA_SCR_TCIE (1U << DMA_SCR_TCIE_BIT)
+#define DMA_SCR_HTIE (1U << DMA_SCR_HTIE_BIT)
+#define DMA_SCR_TEIE (1U << DMA_SCR_TEIE_BIT)
+#define DMA_SCR_DMEIE (1U << DMA_SCR_DMEIE_BIT)
+#define DMA_SCR_EN (1U << DMA_SCR_EN_BIT)
+
+/* Stream FIFO control register */
+
+#define DMA_SFCR_FEIE_BIT 7
+#define DMA_SFCR_DMDIS_BIT 2
+
+#define DMA_SFCR_FEIE (1U << DMA_SFCR_FEIE_BIT)
+#define DMA_SFCR_FS (0x7 << 3)
+#define DMA_SFCR_FS_ZERO_TO_QUARTER (0x0 << 3)
+#define DMA_SFCR_FS_QUARTER_TO_HALF (0x1 << 3)
+#define DMA_SFCR_FS_HALF_TO_THREE_QUARTERS (0x2 << 3)
+#define DMA_SFCR_FS_THREE_QUARTERS_TO_FULL (0x3 << 3)
+#define DMA_SFCR_FS_EMPTY (0x4 << 3)
+#define DMA_SFCR_FS_FULL (0x5 << 3)
+#define DMA_SFCR_DMDIS (1U << DMA_SFCR_DMDIS_BIT)
+#define DMA_SFCR_FTH (0x3 << 0)
+#define DMA_SFCR_FTH_QUARTER_FULL (0x0 << 3)
+#define DMA_SFCR_FTH_HALF_FULL (0x1 << 3)
+#define DMA_SFCR_FTH_THREE_QUARTERS_FULL (0x2 << 3)
+#define DMA_SFCR_FTH_FULL (0x3 << 3)
+
+/*
+ * Devices
+ */
+
+extern dma_dev *DMA1;
+extern dma_dev *DMA2;
+
+/*
+ * Other types needed by, or useful for, <libmaple/dma.h>
+ */
+
+/**
+ * @brief DMA streams
+ * This is also the dma_tube type for STM32F2.
+ * @see dma_tube
+ */
+typedef enum dma_stream {
+ DMA_S0 = 0,
+ DMA_S1 = 1,
+ DMA_S2 = 2,
+ DMA_S3 = 3,
+ DMA_S4 = 4,
+ DMA_S5 = 5,
+ DMA_S6 = 6,
+ DMA_S7 = 7,
+} dma_stream;
+
+/** STM32F2 dma_tube (=dma_stream) */
+#define dma_tube dma_stream
+
+/**
+ * @brief STM32F2 configuration flags for dma_tube_config.
+ * @see struct dma_tube_config
+ */
+typedef enum dma_cfg_flags {
+ /* NB: flags that aren't SCR bits are treated specially. */
+
+ /**
+ * Source address increment mode
+ *
+ * If this flag is set, the source address is incremented (by the
+ * source size) after each DMA transfer.
+ */
+ DMA_CFG_SRC_INC = 1U << 31,
+
+ /**
+ * Destination address increment mode
+ *
+ * If this flag is set, the destination address is incremented (by
+ * the destination size) after each DMA transfer.
+ */
+ DMA_CFG_DST_INC = 1U << 30,
+
+ /**
+ * Circular mode
+ *
+ * This mode is not available for memory-to-memory transfers.
+ */
+ DMA_CFG_CIRC = DMA_SCR_CIRC,
+
+ /** Transfer complete interrupt enable */
+ DMA_CFG_CMPLT_IE = DMA_SCR_TCIE,
+ /** Transfer half-complete interrupt enable */
+ DMA_CFG_HALF_CMPLT_IE = DMA_SCR_HTIE,
+ /** Transfer error interrupt enable */
+ DMA_CFG_ERR_IE = DMA_SCR_TEIE,
+ /** Direct mode error interrupt enable */
+ DMA_CFG_DM_ERR_IE = DMA_SCR_DMEIE,
+ /** FIFO error interrupt enable */
+ DMA_CFG_FIFO_ERR_IE = (1U << 29),
+} dma_cfg_flags;
+
+/**
+ * @brief STM32F2 DMA request sources.
+ *
+ * IMPORTANT:
+ *
+ * 1. On STM32F2, a particular dma_request_src is always tied to a
+ * single DMA controller, but often can be supported by multiple
+ * streams. For example, DMA requests from ADC1 (DMA_REQ_SRC_ADC1) can
+ * only be handled by DMA2, but they can go to either stream 0 or
+ * stream 4 (though not any other stream). If you try to use a request
+ * source with the wrong DMA controller or the wrong stream on
+ * STM32F2, dma_tube_cfg() will fail.
+ *
+ * 2. A single stream can only handle a single request source at a
+ * time. If you change a stream's request source later, it will stop
+ * serving requests from the old source. However, for some streams,
+ * some sources conflict with one another (when they correspond to the
+ * same channel on that stream), and on STM32F2, Terrible Super-Bad
+ * Things will happen if two conflicting request sources are active at
+ * the same time.
+ *
+ * @see struct dma_tube_config
+ * @see dma_tube_cfg()
+ */
+typedef enum dma_request_src {
+ /* These are constructed like so (though this may change, so user
+ * code shouldn't depend on it):
+ *
+ * Bits 0--2: Channel associated with request source
+ *
+ * Bits 3--9: rcc_clk_id of DMA controller associated with request source
+ *
+ * Bits 10--17: Bit mask of streams which can handle that request
+ * source. (E.g., bit 10 set means stream 0 can
+ * handle the source, bit 11 set means stream 1 can,
+ * etc.)
+ *
+ * Among other things, this is used for error checking in
+ * dma_tube_cfg(). If you change this bit encoding, you need to
+ * update the helper functions in stm32f2/dma.c.
+ */
+#define _DMA_STM32F2_REQ_SRC(stream_mask, clk_id, channel) \
+ (((stream_mask) << 10) | ((clk_id) << 3) | (channel))
+#define _DMA_S(n) (1U << (n))
+
+ /* DMA1 request sources */
+#define _DMA_1_REQ_SRC(stream_mask, channel) \
+ _DMA_STM32F2_REQ_SRC(stream_mask, RCC_DMA1, channel)
+
+ /* Channel 0 */
+ DMA_REQ_SRC_SPI3_RX = _DMA_1_REQ_SRC(_DMA_S(0) | _DMA_S(2), 0),
+ DMA_REQ_SRC_SPI2_RX = _DMA_1_REQ_SRC(_DMA_S(3), 0),
+ DMA_REQ_SRC_SPI2_TX = _DMA_1_REQ_SRC(_DMA_S(4), 0),
+ DMA_REQ_SRC_SPI3_TX = _DMA_1_REQ_SRC(_DMA_S(5) | _DMA_S(7), 0),
+
+ /* Channel 1 */
+ DMA_REQ_SRC_I2C1_RX = _DMA_1_REQ_SRC(_DMA_S(0) | _DMA_S(5), 1),
+ DMA_REQ_SRC_TIM7_UP = _DMA_1_REQ_SRC(_DMA_S(2) | _DMA_S(4), 1),
+ DMA_REQ_SRC_I2C1_TX = _DMA_1_REQ_SRC(_DMA_S(6) | _DMA_S(7), 1),
+
+ /* Channel 2 */
+ DMA_REQ_SRC_TIM4_CH1 = _DMA_1_REQ_SRC(_DMA_S(0), 2),
+ DMA_REQ_SRC_TIM4_CH2 = _DMA_1_REQ_SRC(_DMA_S(3), 2),
+ DMA_REQ_SRC_TIM4_UP = _DMA_1_REQ_SRC(_DMA_S(6), 2),
+ DMA_REQ_SRC_TIM4_CH3 = _DMA_1_REQ_SRC(_DMA_S(7), 2),
+
+ /* Channel 3 */
+ DMA_REQ_SRC_TIM2_UP = _DMA_1_REQ_SRC(_DMA_S(1) | _DMA_S(7), 3),
+ DMA_REQ_SRC_TIM2_CH3 = _DMA_1_REQ_SRC(_DMA_S(1), 3),
+ DMA_REQ_SRC_I2C3_RX = _DMA_1_REQ_SRC(_DMA_S(2), 3),
+ DMA_REQ_SRC_I2C3_TX = _DMA_1_REQ_SRC(_DMA_S(4), 3),
+ DMA_REQ_SRC_TIM2_CH1 = _DMA_1_REQ_SRC(_DMA_S(5), 3),
+ DMA_REQ_SRC_TIM2_CH2 = _DMA_1_REQ_SRC(_DMA_S(6), 3),
+ DMA_REQ_SRC_TIM2_CH4 = _DMA_1_REQ_SRC(_DMA_S(6) | _DMA_S(7), 3),
+
+ /* Channel 4 */
+ DMA_REQ_SRC_UART5_RX = _DMA_1_REQ_SRC(_DMA_S(0), 4),
+ DMA_REQ_SRC_USART3_RX = _DMA_1_REQ_SRC(_DMA_S(1), 4),
+ DMA_REQ_SRC_UART4_RX = _DMA_1_REQ_SRC(_DMA_S(2), 4),
+ DMA_REQ_SRC_USART3_TX = _DMA_1_REQ_SRC(_DMA_S(3), 4),
+ DMA_REQ_SRC_UART4_TX = _DMA_1_REQ_SRC(_DMA_S(4), 4),
+ DMA_REQ_SRC_USART2_RX = _DMA_1_REQ_SRC(_DMA_S(5), 4),
+ DMA_REQ_SRC_USART2_TX = _DMA_1_REQ_SRC(_DMA_S(6), 4),
+ DMA_REQ_SRC_UART5_TX = _DMA_1_REQ_SRC(_DMA_S(7), 4),
+
+ /* Channel 5 */
+ DMA_REQ_SRC_TIM3_CH4 = _DMA_1_REQ_SRC(_DMA_S(2), 5),
+ DMA_REQ_SRC_TIM3_UP = _DMA_1_REQ_SRC(_DMA_S(2), 5),
+ DMA_REQ_SRC_TIM3_CH1 = _DMA_1_REQ_SRC(_DMA_S(4), 5),
+ DMA_REQ_SRC_TIM3_TRIG = _DMA_1_REQ_SRC(_DMA_S(4), 5),
+ DMA_REQ_SRC_TIM3_CH2 = _DMA_1_REQ_SRC(_DMA_S(5), 5),
+ DMA_REQ_SRC_TIM3_CH3 = _DMA_1_REQ_SRC(_DMA_S(7), 5),
+
+ /* Channel 6 */
+ DMA_REQ_SRC_TIM5_CH3 = _DMA_1_REQ_SRC(_DMA_S(0), 6),
+ DMA_REQ_SRC_TIM5_UP = _DMA_1_REQ_SRC(_DMA_S(0) | _DMA_S(6), 6),
+ DMA_REQ_SRC_TIM5_CH4 = _DMA_1_REQ_SRC(_DMA_S(1) | _DMA_S(3), 6),
+ DMA_REQ_SRC_TIM5_TRIG = _DMA_1_REQ_SRC(_DMA_S(1) | _DMA_S(3), 6),
+ DMA_REQ_SRC_TIM5_CH1 = _DMA_1_REQ_SRC(_DMA_S(2), 6),
+ DMA_REQ_SRC_TIM5_CH2 = _DMA_1_REQ_SRC(_DMA_S(4), 6),
+
+ /* Channel 7 */
+ DMA_REQ_SRC_TIM6_UP = _DMA_1_REQ_SRC(_DMA_S(1), 7),
+ DMA_REQ_SRC_I2C2_RX = _DMA_1_REQ_SRC(_DMA_S(2) | _DMA_S(3), 7),
+ DMA_REQ_SRC_USART3_TX_ALTERNATE = _DMA_1_REQ_SRC(_DMA_S(4), 7),
+ DMA_REQ_SRC_DAC1 = _DMA_1_REQ_SRC(_DMA_S(5), 7),
+ DMA_REQ_SRC_DAC2 = _DMA_1_REQ_SRC(_DMA_S(6), 7),
+ DMA_REQ_SRC_I2C2_TX = _DMA_1_REQ_SRC(_DMA_S(7), 7),
+#undef _DMA_1_REQ_SRC
+
+ /* DMA2 request sources */
+#define _DMA_2_REQ_SRC(stream_mask, channel) \
+ _DMA_STM32F2_REQ_SRC(stream_mask, RCC_DMA2, channel)
+
+ /* Channel 0 */
+ DMA_REQ_SRC_ADC1 = _DMA_2_REQ_SRC(_DMA_S(0) | _DMA_S(4), 0),
+ /* You can use these "DMA_REQ_SRC_TIMx_CHx_ALTERNATE" if you know
+ * what you're doing, but the other ones (for channels 6 and 7),
+ * are better, in that they don't conflict with one another. */
+ DMA_REQ_SRC_TIM8_CH1_ALTERNATE = _DMA_2_REQ_SRC(_DMA_S(2), 0),
+ DMA_REQ_SRC_TIM8_CH2_ALTERNATE = _DMA_2_REQ_SRC(_DMA_S(2), 0),
+ DMA_REQ_SRC_TIM8_CH3_ALTERNATE = _DMA_2_REQ_SRC(_DMA_S(2), 0),
+ DMA_REQ_SRC_TIM1_CH1_ALTERNATE = _DMA_2_REQ_SRC(_DMA_S(6), 0),
+ DMA_REQ_SRC_TIM1_CH2_ALTERNATE = _DMA_2_REQ_SRC(_DMA_S(6), 0),
+ DMA_REQ_SRC_TIM1_CH3_ALTENRATE = _DMA_2_REQ_SRC(_DMA_S(6), 0),
+
+ /* Channel 1 */
+ DMA_REQ_SRC_DCMI = _DMA_2_REQ_SRC(_DMA_S(1) | _DMA_S(7), 1),
+ DMA_REQ_SRC_ADC2 = _DMA_2_REQ_SRC(_DMA_S(2) | _DMA_S(3), 1),
+
+ /* Channel 2 */
+ DMA_REQ_SRC_ADC3 = _DMA_2_REQ_SRC(_DMA_S(0) | _DMA_S(1), 2),
+ DMA_REQ_SRC_CRYP_OUT = _DMA_2_REQ_SRC(_DMA_S(5), 2),
+ DMA_REQ_SRC_CRYP_IN = _DMA_2_REQ_SRC(_DMA_S(6), 2),
+ DMA_REQ_SRC_HASH_IN = _DMA_2_REQ_SRC(_DMA_S(7), 2),
+
+ /* Channel 3 */
+ DMA_REQ_SRC_SPI1_RX = _DMA_2_REQ_SRC(_DMA_S(0) | _DMA_S(2), 3),
+ DMA_REQ_SRC_SPI1_TX = _DMA_2_REQ_SRC(_DMA_S(3) | _DMA_S(5), 3),
+
+ /* Channel 4 */
+ DMA_REQ_SRC_USART1_RX = _DMA_2_REQ_SRC(_DMA_S(2) | _DMA_S(5), 4),
+ DMA_REQ_SRC_SDIO = _DMA_2_REQ_SRC(_DMA_S(3) | _DMA_S(6), 4),
+ DMA_REQ_SRC_USART1_TX = _DMA_2_REQ_SRC(_DMA_S(7), 4),
+
+ /* Channel 5 */
+ DMA_REQ_SRC_USART6_RX = _DMA_2_REQ_SRC(_DMA_S(1) | _DMA_S(2), 5),
+ DMA_REQ_SRC_USART6_TX = _DMA_2_REQ_SRC(_DMA_S(6) | _DMA_S(7), 5),
+
+ /* Channel 6 */
+ DMA_REQ_SRC_TIM1_TRIG = _DMA_2_REQ_SRC(_DMA_S(0) | _DMA_S(4), 6),
+ DMA_REQ_SRC_TIM1_CH1 = _DMA_2_REQ_SRC(_DMA_S(1) | _DMA_S(3), 6),
+ DMA_REQ_SRC_TIM1_CH2 = _DMA_2_REQ_SRC(_DMA_S(3), 6),
+ DMA_REQ_SRC_TIM1_CH4 = _DMA_2_REQ_SRC(_DMA_S(4), 6),
+ DMA_REQ_SRC_TIM1_COM = _DMA_2_REQ_SRC(_DMA_S(4), 6),
+ DMA_REQ_SRC_TIM1_UP = _DMA_2_REQ_SRC(_DMA_S(5), 6),
+ DMA_REQ_SRC_TIM1_CH3 = _DMA_2_REQ_SRC(_DMA_S(6), 6),
+
+ /* Channel 7 */
+ DMA_REQ_SRC_TIM8_UP = _DMA_2_REQ_SRC(_DMA_S(1), 7),
+ DMA_REQ_SRC_TIM8_CH1 = _DMA_2_REQ_SRC(_DMA_S(2), 7),
+ DMA_REQ_SRC_TIM8_CH2 = _DMA_2_REQ_SRC(_DMA_S(3), 7),
+ DMA_REQ_SRC_TIM8_CH3 = _DMA_2_REQ_SRC(_DMA_S(4), 7),
+ DMA_REQ_SRC_TIM8_CH4 = _DMA_2_REQ_SRC(_DMA_S(7), 7),
+ DMA_REQ_SRC_TIM8_TRIG = _DMA_2_REQ_SRC(_DMA_S(7), 7),
+ DMA_REQ_SRC_TIM8_COM = _DMA_2_REQ_SRC(_DMA_S(7), 7),
+#undef _DMA_2_REQ_SRC
+#undef _DMA_S
+} dma_request_src;
+
+/*
+ * Tube conveniences
+ */
+
+static inline dma_tube_reg_map* dma_tube_regs(dma_dev *dev,
+ dma_tube tube) {
+ ASSERT(DMA_S0 <= tube && tube <= DMA_S7);
+ switch (dev->clk_id) {
+ case RCC_DMA1:
+ return DMA1S0_BASE + (int)tube;
+ case RCC_DMA2:
+ return DMA2S0_BASE + (int)tube;
+ default:
+ /* Can't happen */
+ ASSERT(0);
+ return 0;
+ }
+}
+
+static inline uint8 dma_is_enabled(dma_dev *dev, dma_tube tube) {
+ return dma_tube_regs(dev, tube)->SCR & DMA_SCR_EN;
+}
+
+/* F2-only; available because of double-buffering. */
+void dma_set_mem_n_addr(dma_dev *dev, dma_tube tube, int n,
+ __io void *address);
+
+/**
+ * @brief Set memory 0 address.
+ * Availability: STM32F2.
+ *
+ * @param dev DMA device
+ * @param tube Tube whose memory 0 address to set
+ * @param addr Address to use as memory 0
+ */
+static __always_inline void
+dma_set_mem0_addr(dma_dev *dev, dma_tube tube, __io void *addr) {
+ dma_set_mem_n_addr(dev, tube, 0, addr);
+}
+
+/**
+ * @brief Set memory 1 address.
+ * Availability: STM32F2.
+ *
+ * @param dev DMA device
+ * @param tube Tube whose memory 1 address to set
+ * @param addr Address to use as memory 1
+ */
+static __always_inline void
+dma_set_mem1_addr(dma_dev *dev, dma_tube tube, __io void *addr) {
+ dma_set_mem_n_addr(dev, tube, 1, addr);
+}
+
+/* Assume the user means SM0AR in a non-double-buffered configuration. */
+static __always_inline void
+dma_set_mem_addr(dma_dev *dev, dma_tube tube, __io void *addr) {
+ dma_set_mem0_addr(dev, tube, addr);
+}
+
+/* SM0AR and SM1AR are treated as though they have the same size */
+static inline dma_xfer_size dma_get_mem_size(dma_dev *dev, dma_tube tube) {
+ return (dma_xfer_size)(dma_tube_regs(dev, tube)->SCR >> 13);
+}
+
+static inline dma_xfer_size dma_get_per_size(dma_dev *dev, dma_tube tube) {
+ return (dma_xfer_size)(dma_tube_regs(dev, tube)->SCR >> 11);
+}
+
+void dma_enable_fifo(dma_dev *dev, dma_tube tube);
+void dma_disable_fifo(dma_dev *dev, dma_tube tube);
+
+static __always_inline int dma_is_fifo_enabled(dma_dev *dev, dma_tube tube) {
+ return dma_tube_regs(dev, tube)->SFCR & DMA_SFCR_DMDIS;
+}
+
+/*
+ * TODO:
+ * - Double-buffer configuration function
+ * - FIFO configuration function
+ * - MBURST/PBURST configuration function
+ */
+
+/*
+ * ISR/IFCR conveniences.
+ */
+
+/* (undocumented) helper for reading LISR/HISR and writing
+ * LIFCR/HIFCR. For these registers,
+ *
+ * S0, S4: bits start at bit 0
+ * S1, S5: 6
+ * S2, S6: 16
+ * S3, S7: 22
+ *
+ * I can't imagine why ST didn't just use a byte for each group. The
+ * bits fit, and it would have made functions like these simpler and
+ * faster. Oh well. */
+static __always_inline uint32 _dma_sr_fcr_shift(dma_tube tube) {
+ switch (tube) {
+ case DMA_S0: /* fall through */
+ case DMA_S4:
+ return 0;
+ case DMA_S1: /* fall through */
+ case DMA_S5:
+ return 6;
+ case DMA_S2: /* fall through */
+ case DMA_S6:
+ return 16;
+ case DMA_S3: /* fall through */
+ case DMA_S7:
+ return 22;
+ }
+ /* Can't happen */
+ ASSERT(0);
+ return 0;
+}
+
+static inline uint8 dma_get_isr_bits(dma_dev *dev, dma_tube tube) {
+ dma_reg_map *regs = dev->regs;
+ __io uint32 *isr = tube > DMA_S3 ? &regs->HISR : &regs->LISR;
+ return (*isr >> _dma_sr_fcr_shift(tube)) & 0x3D;
+}
+
+static inline void dma_clear_isr_bits(dma_dev *dev, dma_tube tube) {
+ dma_reg_map *regs = dev->regs;
+ __io uint32 *ifcr = tube > DMA_S3 ? &regs->HIFCR : &regs->LIFCR;
+ *ifcr = (0x3D << _dma_sr_fcr_shift(tube));
+}
+
+#undef _DMA_IRQ_BIT_SHIFT
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
diff --git a/libmaple/stm32f2/rules.mk b/libmaple/stm32f2/rules.mk
index 759f905..4c62cc2 100644
--- a/libmaple/stm32f2/rules.mk
+++ b/libmaple/stm32f2/rules.mk
@@ -12,6 +12,7 @@ sSRCS_$(d) := isrs.S
sSRCS_$(d) += vector_table.S
cSRCS_$(d) := adc.c
+cSRCS_$(d) += dma.c
cSRCS_$(d) += exti.c
cSRCS_$(d) += fsmc.c
cSRCS_$(d) += gpio.c