diff options
Diffstat (limited to 'libmaple/stm32f2/dma.c')
-rw-r--r-- | libmaple/stm32f2/dma.c | 504 |
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; -} |