aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple/dma.c
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2012-04-26 17:33:56 -0400
committerMarti Bolivar <mbolivar@leaflabs.com>2012-05-03 14:09:05 -0400
commit1f67ed8f23048c45b0a3deec232b19047966bce7 (patch)
treeda3aec665bda333922c249f5094942ee3da7eb46 /libmaple/dma.c
parenta81a02257d69454412d4ef062b56a3cc5c758815 (diff)
downloadlibrambutan-1f67ed8f23048c45b0a3deec232b19047966bce7.tar.gz
librambutan-1f67ed8f23048c45b0a3deec232b19047966bce7.zip
stm32f1: Resurrect DMA support. (sets up breaking change)
Breaking change set up: struct dma_handler_config is no longer part of the public API in <libmaple/dma.h>. User code which was touching these was always mistaken; it should be using dma_attach_interrupt() or dma_detach_interrupt() instead. Other than that, just move the nonportable bits in <libmaple/dma.h> and libmaple/dma.c to the appropriate places under libmaple/stm32f1/. (Ouch. This is almost everything.) Patch the (new) STM32F1 <series/dma.h> here and there to make everything compile; this is mostly limited to forward-declaring struct dma_dev and providing a hack _dma_dev_regs() declaration so inline functions in the series header can still access a device's registers. Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
Diffstat (limited to 'libmaple/dma.c')
-rw-r--r--libmaple/dma.c338
1 files changed, 3 insertions, 335 deletions
diff --git a/libmaple/dma.c b/libmaple/dma.c
index f20613b..6442e4d 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,13 @@
*****************************************************************************/
/**
- * @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 <libmaple/dma.h>
-#include <libmaple/bitband.h>
-#include <libmaple/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
/*
* Convenience routines
@@ -78,302 +45,3 @@ dma_dev *DMA2 = &dma2;
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
- */
-
-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 */
- }
-}
-
-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