1 /*
2  * Copyright (c) 2016 Erik Gilling
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 #include <platform/dma.h>
9 
10 #include <assert.h>
11 #include <lk/compiler.h>
12 
13 #include <arch/arm/cm.h>
14 #include <kernel/event.h>
15 #include <platform/rcc.h>
16 #include <sys/types.h>
17 
18 #include <stm32f0xx.h>
19 
20 typedef DMA_Channel_TypeDef dma_channel_regs_t;
21 
22 event_t dma_events[DMA_CHANNELS];
23 
dma_channel_assert(dma_channel_t chan)24 static void dma_channel_assert(dma_channel_t chan) {
25     assert(DMA_CHANNEL_1 <= chan && chan < DMA_CHANNEL_7);
26 }
27 
dma_get_channel(dma_channel_t chan)28 static dma_channel_regs_t *dma_get_channel(dma_channel_t chan) {
29     unsigned long addr =
30         DMA1_Channel1_BASE + (chan - 1) * 0x14;
31     return (dma_channel_regs_t *)addr;
32 }
33 
dma_event(dma_channel_t chan)34 static event_t *dma_event(dma_channel_t chan) {
35     return &dma_events[chan - 1];
36 }
37 
38 // TODO(konkers): Separate out DMA IRQ handling by channel group.
dma_irq(void)39 void dma_irq(void) {
40     arm_cm_irq_entry();
41     bool resched = false;
42 
43     uint32_t sr = DMA1->ISR;
44 
45     size_t i;
46     for (i = 0; i < countof(dma_events); i++) {
47         uint32_t ch_sr = (sr >> (i * 4)) & 0xf;
48 
49         // TODO(konkers): Report error.
50         if (ch_sr & (DMA_ISR_TCIF1 | DMA_ISR_TEIF1)) {
51             event_signal(&dma_events[i], false);
52             resched = true;
53         }
54     }
55     DMA1->IFCR = sr;
56     arm_cm_irq_exit(resched);
57 }
58 
stm32_DMA1_Channel1_IRQ(void)59 void stm32_DMA1_Channel1_IRQ(void) {
60     dma_irq();
61 }
62 
stm32_DMA1_Channel2_3_IRQ(void)63 void stm32_DMA1_Channel2_3_IRQ(void) {
64     dma_irq();
65 }
66 
stm32_DMA1_Channel4_5_6_7_IRQ(void)67 void stm32_DMA1_Channel4_5_6_7_IRQ(void) {
68     dma_irq();
69 }
70 
dma_transfer_start(dma_channel_t chan,uint32_t periph_addr,uint32_t mem_addr,uint16_t count,uint32_t flags)71 void dma_transfer_start(dma_channel_t chan,
72                         uint32_t periph_addr,
73                         uint32_t mem_addr,
74                         uint16_t count,
75                         uint32_t flags) {
76     dma_channel_assert(chan);
77     event_unsignal(dma_event(chan));
78 
79     dma_channel_regs_t *chan_regs = dma_get_channel(chan);
80     chan_regs->CCR &= ~DMA_CCR_EN;
81 
82     chan_regs->CPAR = periph_addr;
83     chan_regs->CMAR = mem_addr;
84     chan_regs->CNDTR = count;
85     chan_regs->CCR = flags | DMA_CCR_TEIE | DMA_CCR_TCIE | DMA_CCR_EN;
86 }
87 
dma_wait(dma_channel_t chan)88 void dma_wait(dma_channel_t chan) {
89     dma_channel_assert(chan);
90 
91     event_wait(dma_event(chan));
92 }
93 
dma_init(void)94 void dma_init(void) {
95     stm32_rcc_set_enable(STM32_RCC_CLK_DMA, true);
96 
97     size_t i;
98     for (i = 0; i < countof(dma_events); i++) {
99         // Initialize all the channel events as signaled because they're idle.
100         event_init(&dma_events[i], true, 0);
101     }
102     NVIC_EnableIRQ(DMA1_Channel1_IRQn);
103     NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
104     NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn);
105 }
106