aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple/dma.c
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2012-06-26 18:24:49 -0400
committerMarti Bolivar <mbolivar@leaflabs.com>2012-06-26 18:32:57 -0400
commitf005bd3a5c087e3d5559f2858a1e7898a4f92a8d (patch)
tree0701628a68056f7b5f92d5a5af5f281f58e6a71e /libmaple/dma.c
parent761e059962e8f53f3cceef61d65bf2bf3025319a (diff)
parentc6073e4886da4606679bc3e9d770c9cff9390597 (diff)
downloadlibrambutan-f005bd3a5c087e3d5559f2858a1e7898a4f92a8d.tar.gz
librambutan-f005bd3a5c087e3d5559f2858a1e7898a4f92a8d.zip
Merge branch 'wip-family-support'
Merge the long-lived (too long; future changes like these will need to proceed more incrementally) development branch of libmaple, containing experimental STM32F2 and STM32F1 value line support, into master. This required many changes to the structure of the library. The most important structural reorganizations occurred in: - 954f9e5: moves public headers to include directories - 3efa313: uses "series" instead of "family" - c0d60e3: adds board files to the build system, to make it easier to add new boards - 096d86c: adds build logic for targeting different STM32 series (e.g. STM32F1, STM32F2) This last commit in particular (096d86c) is the basis for the repartitioning of libmaple into portable sections, which work on all supported MCUs, and nonportable sections, which are segregated into separate directories and contain all series-specific code. Moving existing STM32F1-only code into libmaple/stm32f1 and wirish/stm32f1, along with adding equivalents under .../stm32f2 directories, was the principal project of this branch. Important API changes occur in several places. Existing code is still expected to work on STM32F1 targets, but there have been many deprecations. A detailed changelog explaining the situation needs to be prepared. F2 and F1 value line support is not complete; the merge is proceeding prematurely in this respect. We've been getting more libmaple patches from the community lately, and I'm worried that the merge conflicts with the old tree structure will become painful to manage. Conflicts: Makefile Resolved Makefile conflicts manually; this required propagating -Xlinker usage into support/make/target-config.mk. Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
Diffstat (limited to 'libmaple/dma.c')
-rw-r--r--libmaple/dma.c363
1 files changed, 33 insertions, 330 deletions
diff --git a/libmaple/dma.c b/libmaple/dma.c
index 60f4d47..d13de10 100644
--- a/libmaple/dma.c
+++ b/libmaple/dma.c
@@ -2,6 +2,7 @@
* The MIT License
*
* Copyright (c) 2010 Michael Hope.
+ * Copyright (c) 2012 LeafLabs, LLC.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -25,47 +26,15 @@
*****************************************************************************/
/**
- * @file dma.c
+ * @file libmaple/dma.c
* @author Marti Bolivar <mbolivar@leaflabs.com>;
* Original implementation by Michael Hope
- * @brief Direct Memory Access peripheral support
+ * @brief Portable DMA routines.
*/
-#include "dma.h"
-#include "bitband.h"
-#include "util.h"
-
-/*
- * Devices
- */
-
-static dma_dev dma1 = {
- .regs = DMA1_BASE,
- .clk_id = RCC_DMA1,
- .handlers = {{ .handler = NULL, .irq_line = NVIC_DMA_CH1 },
- { .handler = NULL, .irq_line = NVIC_DMA_CH2 },
- { .handler = NULL, .irq_line = NVIC_DMA_CH3 },
- { .handler = NULL, .irq_line = NVIC_DMA_CH4 },
- { .handler = NULL, .irq_line = NVIC_DMA_CH5 },
- { .handler = NULL, .irq_line = NVIC_DMA_CH6 },
- { .handler = NULL, .irq_line = NVIC_DMA_CH7 }}
-};
-/** DMA1 device */
-dma_dev *DMA1 = &dma1;
-
-#ifdef STM32_HIGH_DENSITY
-static dma_dev dma2 = {
- .regs = DMA2_BASE,
- .clk_id = RCC_DMA2,
- .handlers = {{ .handler = NULL, .irq_line = NVIC_DMA2_CH1 },
- { .handler = NULL, .irq_line = NVIC_DMA2_CH2 },
- { .handler = NULL, .irq_line = NVIC_DMA2_CH3 },
- { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 },
- { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 }} /* !@#$ */
-};
-/** DMA2 device */
-dma_dev *DMA2 = &dma2;
-#endif
+#include <libmaple/dma.h>
+#include "dma_private.h"
+#include "stm32_private.h"
/*
* Convenience routines
@@ -79,301 +48,35 @@ void dma_init(dma_dev *dev) {
rcc_clk_enable(dev->clk_id);
}
-/**
- * @brief Set up a DMA transfer.
- *
- * The channel will be disabled before being reconfigured. The
- * transfer will have low priority by default. You may choose another
- * priority before the transfer begins using dma_set_priority(), as
- * well as performing any other configuration you desire. When the
- * channel is configured to your liking, enable it using dma_enable().
- *
- * @param dev DMA device.
- * @param channel DMA channel.
- * @param peripheral_address Base address of peripheral data register
- * involved in the transfer.
- * @param peripheral_size Peripheral data transfer size.
- * @param memory_address Base memory address involved in the transfer.
- * @param memory_size Memory data transfer size.
- * @param mode Logical OR of dma_mode_flags
- * @sideeffect Disables the given DMA channel.
- * @see dma_xfer_size
- * @see dma_mode_flags
- * @see dma_set_num_transfers()
- * @see dma_set_priority()
- * @see dma_attach_interrupt()
- * @see dma_enable()
- */
-void dma_setup_transfer(dma_dev *dev,
- dma_channel channel,
- __io void *peripheral_address,
- dma_xfer_size peripheral_size,
- __io void *memory_address,
- dma_xfer_size memory_size,
- uint32 mode) {
- dma_channel_reg_map *channel_regs = dma_channel_regs(dev, channel);
-
- dma_disable(dev, channel); /* can't write to CMAR/CPAR otherwise */
- channel_regs->CCR = (memory_size << 10) | (peripheral_size << 8) | mode;
- channel_regs->CMAR = (uint32)memory_address;
- channel_regs->CPAR = (uint32)peripheral_address;
-}
-
-/**
- * @brief Set the number of data to be transferred on a DMA channel.
- *
- * You may not call this function while the channel is enabled.
- *
- * @param dev DMA device
- * @param channel Channel through which the transfer occurs.
- * @param num_transfers
- */
-void dma_set_num_transfers(dma_dev *dev,
- dma_channel channel,
- uint16 num_transfers) {
- dma_channel_reg_map *channel_regs;
-
- ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
-
- channel_regs = dma_channel_regs(dev, channel);
- channel_regs->CNDTR = num_transfers;
-}
-
-/**
- * @brief Set the priority of a DMA transfer.
- *
- * You may not call this function while the channel is enabled.
- *
- * @param dev DMA device
- * @param channel DMA channel
- * @param priority priority to set.
- */
-void dma_set_priority(dma_dev *dev,
- dma_channel channel,
- dma_priority priority) {
- dma_channel_reg_map *channel_regs;
- uint32 ccr;
-
- ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
-
- channel_regs = dma_channel_regs(dev, channel);
- ccr = channel_regs->CCR;
- ccr &= ~DMA_CCR_PL;
- ccr |= priority;
- channel_regs->CCR = ccr;
-}
-
-/**
- * @brief Attach an interrupt to a DMA transfer.
- *
- * Interrupts are enabled using appropriate mode flags in
- * dma_setup_transfer().
- *
- * @param dev DMA device
- * @param channel Channel to attach handler to
- * @param handler Interrupt handler to call when channel interrupt fires.
- * @see dma_setup_transfer()
- * @see dma_get_irq_cause()
- * @see dma_detach_interrupt()
- */
-void dma_attach_interrupt(dma_dev *dev,
- dma_channel channel,
- void (*handler)(void)) {
- dev->handlers[channel - 1].handler = handler;
- nvic_irq_enable(dev->handlers[channel - 1].irq_line);
-}
-
-/**
- * @brief Detach a DMA transfer interrupt handler.
- *
- * After calling this function, the given channel's interrupts will be
- * disabled.
- *
- * @param dev DMA device
- * @param channel Channel whose handler to detach
- * @sideeffect Clears interrupt enable bits in the channel's CCR register.
- * @see dma_attach_interrupt()
- */
-void dma_detach_interrupt(dma_dev *dev, dma_channel channel) {
- /* Don't use nvic_irq_disable()! Think about DMA2 channels 4 and 5. */
- dma_channel_regs(dev, channel)->CCR &= ~0xF;
- dev->handlers[channel - 1].handler = NULL;
-}
-
-/**
- * @brief Discover the reason why a DMA interrupt was called.
- *
- * You may only call this function within an attached interrupt
- * handler for the given channel.
- *
- * This function resets the internal DMA register state which encodes
- * the cause of the interrupt; consequently, it can only be called
- * once per interrupt handler invocation.
- *
- * @param dev DMA device
- * @param channel Channel whose interrupt is being handled.
- * @return Reason why the interrupt fired.
- * @sideeffect Clears channel status flags in dev->regs->ISR.
- * @see dma_attach_interrupt()
- * @see dma_irq_cause
- */
-dma_irq_cause dma_get_irq_cause(dma_dev *dev, dma_channel channel) {
- uint8 status_bits = dma_get_isr_bits(dev, channel);
-
- /* If the channel global interrupt flag is cleared, then
- * something's very wrong. */
- ASSERT(status_bits & BIT(0));
-
- dma_clear_isr_bits(dev, channel);
-
- /* ISR flags get set even if the corresponding interrupt enable
- * bits in the channel's configuration register are cleared, so we
- * can't use a switch here.
- *
- * Don't change the order of these if statements. */
- if (status_bits & BIT(3)) {
- return DMA_TRANSFER_ERROR;
- } else if (status_bits & BIT(1)) {
- return DMA_TRANSFER_COMPLETE;
- } else if (status_bits & BIT(2)) {
- return DMA_TRANSFER_HALF_COMPLETE;
- } else if (status_bits & BIT(0)) {
- /* Shouldn't happen (unless someone messed up an IFCR write). */
- throb();
- }
-#if DEBUG_LEVEL < DEBUG_ALL
- else {
- /* We shouldn't have been called, but the debug level is too
- * low for the above ASSERT() to have had any effect. In
- * order to fail fast, mimic the DMA controller's behavior
- * when an error occurs. */
- dma_disable(dev, channel);
- }
-#endif
- return DMA_TRANSFER_ERROR;
-}
-
-/**
- * @brief Enable a DMA channel.
- * @param dev DMA device
- * @param channel Channel to enable
- */
-void dma_enable(dma_dev *dev, dma_channel channel) {
- dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel);
- bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 1);
-}
-
-/**
- * @brief Disable a DMA channel.
- * @param dev DMA device
- * @param channel Channel to disable
- */
-void dma_disable(dma_dev *dev, dma_channel channel) {
- dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel);
- bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 0);
-}
-
-/**
- * @brief Set the base memory address where data will be read from or
- * written to.
- *
- * You must not call this function while the channel is enabled.
- *
- * If the DMA memory size is 16 bits, the address is automatically
- * aligned to a half-word. If the DMA memory size is 32 bits, the
- * address is aligned to a word.
- *
- * @param dev DMA Device
- * @param channel Channel whose base memory address to set.
- * @param addr Memory base address to use.
- */
-void dma_set_mem_addr(dma_dev *dev, dma_channel channel, __io void *addr) {
- dma_channel_reg_map *chan_regs;
-
- ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
-
- chan_regs = dma_channel_regs(dev, channel);
- chan_regs->CMAR = (uint32)addr;
-}
-
-/**
- * @brief Set the base peripheral address where data will be read from
- * or written to.
- *
- * You must not call this function while the channel is enabled.
- *
- * If the DMA peripheral size is 16 bits, the address is automatically
- * aligned to a half-word. If the DMA peripheral size is 32 bits, the
- * address is aligned to a word.
- *
- * @param dev DMA Device
- * @param channel Channel whose peripheral data register base address to set.
- * @param addr Peripheral memory base address to use.
- */
-void dma_set_per_addr(dma_dev *dev, dma_channel channel, __io void *addr) {
- dma_channel_reg_map *chan_regs;
-
- ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
-
- chan_regs = dma_channel_regs(dev, channel);
- chan_regs->CPAR = (uint32)addr;
-}
-
/*
- * IRQ handlers
+ * Private API
*/
-static inline void dispatch_handler(dma_dev *dev, dma_channel channel) {
- void (*handler)(void) = dev->handlers[channel - 1].handler;
- if (handler) {
- handler();
- dma_clear_isr_bits(dev, channel); /* in case handler doesn't */
+enum dma_atype _dma_addr_type(__io void *addr) {
+ switch (stm32_block_purpose((void*)addr)) {
+ /* Notice we're treating the code block as memory here. That's
+ * correct for addresses in Flash and in [0x0, 0x7FFFFFF]
+ * (provided that those addresses are aliased to Flash, SRAM, or
+ * FSMC, depending on BOOT[01] and possibly SYSCFG_MEMRMP). It's
+ * not correct for other addresses in the code block, but those
+ * will (hopefully) just fail-fast with transfer or bus errors. If
+ * lots of people get confused, it might be worth being more
+ * careful here. */
+ case STM32_BLOCK_CODE: /* Fall through */
+ case STM32_BLOCK_SRAM: /* ... */
+ case STM32_BLOCK_FSMC_1_2: /* ... */
+ case STM32_BLOCK_FSMC_3_4:
+ return DMA_ATYPE_MEM;
+ case STM32_BLOCK_PERIPH:
+ return DMA_ATYPE_PER;
+ case STM32_BLOCK_FSMC_REG: /* Fall through */
+ /* Is this right? I can't think of a reason to DMA into or out
+ * of the FSMC registers. [mbolivar] */
+ case STM32_BLOCK_UNUSED: /* ... */
+ case STM32_BLOCK_CORTEX_INTERNAL: /* ... */
+ return DMA_ATYPE_OTHER;
+ default:
+ ASSERT(0); /* Can't happen */
+ return DMA_ATYPE_OTHER;
}
}
-
-void __irq_dma1_channel1(void) {
- dispatch_handler(DMA1, DMA_CH1);
-}
-
-void __irq_dma1_channel2(void) {
- dispatch_handler(DMA1, DMA_CH2);
-}
-
-void __irq_dma1_channel3(void) {
- dispatch_handler(DMA1, DMA_CH3);
-}
-
-void __irq_dma1_channel4(void) {
- dispatch_handler(DMA1, DMA_CH4);
-}
-
-void __irq_dma1_channel5(void) {
- dispatch_handler(DMA1, DMA_CH5);
-}
-
-void __irq_dma1_channel6(void) {
- dispatch_handler(DMA1, DMA_CH6);
-}
-
-void __irq_dma1_channel7(void) {
- dispatch_handler(DMA1, DMA_CH7);
-}
-
-#ifdef STM32_HIGH_DENSITY
-void __irq_dma2_channel1(void) {
- dispatch_handler(DMA2, DMA_CH1);
-}
-
-void __irq_dma2_channel2(void) {
- dispatch_handler(DMA2, DMA_CH2);
-}
-
-void __irq_dma2_channel3(void) {
- dispatch_handler(DMA2, DMA_CH3);
-}
-
-void __irq_dma2_channel4_5(void) {
- dispatch_handler(DMA2, DMA_CH4);
- dispatch_handler(DMA2, DMA_CH5);
-}
-#endif