aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple/stm32f2/dma.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmaple/stm32f2/dma.c')
-rw-r--r--libmaple/stm32f2/dma.c504
1 files changed, 0 insertions, 504 deletions
diff --git a/libmaple/stm32f2/dma.c b/libmaple/stm32f2/dma.c
deleted file mode 100644
index 26e87b9..0000000
--- a/libmaple/stm32f2/dma.c
+++ /dev/null
@@ -1,504 +0,0 @@
-/******************************************************************************
- * 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;
-}