aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple/dma.c
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@mit.edu>2010-12-29 21:30:42 -0500
committerMarti Bolivar <mbolivar@mit.edu>2010-12-30 02:40:16 -0500
commit8e973f3d1ef0324e213824dc05af0f9713e7b3cb (patch)
treeb5ff3aebf87116b5de0e968a1f44309478a6c430 /libmaple/dma.c
parentd1a8d832af96efdd1b399799dfae81517dc04dfa (diff)
downloadlibrambutan-8e973f3d1ef0324e213824dc05af0f9713e7b3cb.tar.gz
librambutan-8e973f3d1ef0324e213824dc05af0f9713e7b3cb.zip
Changed nzmichaelh's initial DMA interface to be more flexible.
Some bugfixes in the external interrupt code were found along the way. Defines for nonexistent registers removed from nvic interface.
Diffstat (limited to 'libmaple/dma.c')
-rw-r--r--libmaple/dma.c112
1 files changed, 77 insertions, 35 deletions
diff --git a/libmaple/dma.c b/libmaple/dma.c
index 88c1342..a6c6ccd 100644
--- a/libmaple/dma.c
+++ b/libmaple/dma.c
@@ -31,76 +31,118 @@
#include "libmaple.h"
#include "dma.h"
#include "rcc.h"
+#include "nvic.h"
#define DMA_EN BIT(0)
-typedef struct dma_regs
-{
+typedef struct dma_regs {
uint32 CCR;
uint32 CNDTR;
uint32 CPAR;
uint32 CMAR;
} dma_regs;
+typedef struct DMAChannel {
+ void (*handler)(void);
+ uint32 irq_line;
+} DMAChannel;
+
+static DMAChannel dma_channels[] = {
+ { .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 }
+};
+
/** Get the base address of the given channel, asserting and returning
* NULL on illegal
*/
static dma_regs *dma_get_regs(uint8 channel) {
if (channel >= 1 && channel <= 7) {
return (dma_regs *)(DMA1_CCR1 + DMA_CHANNEL_STRIDE * (channel-1));
- }
- else {
+ } else {
ASSERT(false);
return NULL;
}
}
-/** Initialise a DMA channel. Start the transfer using dma_start().
- *
- * @param channel the channel number (1..7)
- * @param peripheral address of the peripheral
- * @param from_peripheral true if transfer goes from the peripheral
- * to memory
- * @param mode OR of the dma_mode_flags
- */
-void dma_init(uint8 channel, volatile void *peripheral, int from_peripheral,
+/* Zero-based indexing */
+static inline void dispatch_handler(uint8 channel_idx) {
+ ASSERT(dma_channels[channel_idx].handler);
+ if (dma_channels[channel_idx].handler) {
+ (dma_channels[channel_idx].handler)();
+ }
+}
+
+void DMAChannel1_IRQHandler(void) {
+ dispatch_handler(0);
+}
+
+void DMAChannel2_IRQHandler(void) {
+ dispatch_handler(1);
+}
+
+void DMAChannel3_IRQHandler(void) {
+ dispatch_handler(2);
+}
+
+void DMAChannel4_IRQHandler(void) {
+ dispatch_handler(3);
+}
+
+void DMAChannel5_IRQHandler(void) {
+ dispatch_handler(4);
+}
+
+void DMAChannel6_IRQHandler(void) {
+ dispatch_handler(5);
+}
+
+void DMAChannel7_IRQHandler(void) {
+ dispatch_handler(6);
+}
+
+void dma_init(uint8 channel, volatile void *paddr,
+ dma_transfer_size psize, dma_transfer_size msize,
dma_mode_flags mode) {
volatile dma_regs *regs = dma_get_regs(channel);
if (regs != NULL) {
rcc_clk_enable(RCC_DMA1);
- /* Disable the channel. PENDING: May not be needed */
- regs->CCR = 0;
+ regs->CCR = ((0 << 12) /* Low priority */
+ | (msize << 10)
+ | (psize << 8)
+ | (0 << 0) /* Disabled (until started) */
+ | mode);
- uint32 cr = 0
- | (0 << 12) // Low priority
- | (0 << 10) // Memory size = 8 bits
- | (1 << 8) // Peripheral size = 16 bits
- | (mode << 5) // Increment and circular modes
- | (0 << 0); // Not enabled
-
- /* FIXME XXX integer from pointer without a cast. */
- regs->CPAR = peripheral;
-
- if (!from_peripheral) {
- cr |= 1 << 4; // From memory
- }
-
- /* Stay disabled until started */
- regs->CCR = cr;
+ regs->CPAR = (uint32)paddr;
}
}
-void dma_start(uint8 channel, volatile void *buffer, uint16 count)
-{
+void dma_start(uint8 channel, volatile void *buffer, uint16 count) {
volatile dma_regs *regs = dma_get_regs(channel);
if (regs != NULL) {
- regs->CCR &= ~DMA_EN;
+ regs->CCR &= ~DMA_EN; /* CMAR may not be written with EN set */
regs->CMAR = (uint32)buffer;
regs->CNDTR = count;
- regs->CCR |= DMA_EN;
+ regs->CCR |= DMA_EN; /* Start the transfer */
}
}
+
+void dma_attach_interrupt(uint8 channel, voidFuncPtr handler) {
+ channel--; /* 1-based -> 0-based indexing */
+ dma_channels[channel].handler = handler;
+ nvic_irq_enable(dma_channels[channel].irq_line);
+}
+
+void dma_detach_interrupt(uint8 channel) {
+ channel--;
+ nvic_irq_disable(dma_channels[channel].irq_line);
+ dma_channels[channel].handler = NULL;
+}