60bf34f14aa909c29ec46070df25a33c9e1e7d04
[fw/altos] / src / samd21 / ao_dma_samd21.c
1 /*
2  * Copyright © 2019 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18
19 #include <ao.h>
20 #include <ao_dma-samd21.h>
21
22 static struct samd21_dmac_desc samd21_dmac_desc[SAMD21_DMAC_NCHAN]  __attribute__((aligned(16)));
23 static struct samd21_dmac_desc samd21_dmac_wrb[SAMD21_DMAC_NCHAN]  __attribute__((aligned(16)));
24
25 static volatile uint16_t        saved_intpend;
26 static volatile int             interrupts;
27
28 static struct {
29         void (*callback)(uint8_t id, void *closure);
30         void *closure;
31 } dmac_callback[SAMD21_DMAC_NCHAN];
32
33 void
34 samd21_dmac_isr(void)
35 {
36         uint16_t        intpend = samd21_dmac.intpend;
37
38         ++interrupts;
39         saved_intpend = intpend;
40         if (intpend & 0xff00) {
41                 uint8_t id = (intpend >> SAMD21_DMAC_INTPEND_ID) & SAMD21_DMAC_INTPEND_ID_MASK;
42                 samd21_dmac.intpend = intpend;
43                 if (intpend & (1 << SAMD21_DMAC_INTPEND_TCMPL)) {
44                         if (dmac_callback[id].callback)
45                                 (*dmac_callback[id].callback)(id, dmac_callback[id].closure);
46                 }
47         }
48 }
49
50 void
51 ao_dma_dump(char *where)
52 {
53         printf("DMA %s ctrl %04x intpend %04x intstatus %04x\n",
54                where,
55                samd21_dmac.ctrl,
56                samd21_dmac.intpend,
57                samd21_dmac.intstatus);
58         fflush(stdout);
59         printf(" busych %04x pendch %04x active %08x chctrla %02x\n",
60                samd21_dmac.busych,
61                samd21_dmac.pendch,
62                samd21_dmac.active,
63                samd21_dmac.chctrla);
64         fflush(stdout);
65         printf(" chctrlb %08x chintflag %02x chstatus %02x\n",
66                samd21_dmac.chctrlb,
67                samd21_dmac.chintflag,
68                samd21_dmac.chstatus);
69         fflush(stdout);
70         printf(" btctrl %04x btcnt %04x srcaddr %08x dstaddr %08x descaddr %08x\n",
71                samd21_dmac_desc[0].btctrl,
72                samd21_dmac_desc[0].btcnt,
73                samd21_dmac_desc[0].srcaddr,
74                samd21_dmac_desc[0].dstaddr,
75                samd21_dmac_desc[0].descaddr);
76         fflush(stdout);
77         printf("intpend %04x interrupts %d\n", saved_intpend, interrupts);
78 }
79
80 void
81 _ao_dma_start_transfer(uint8_t          id,
82                        void             *src,
83                        void             *dst,
84                        uint16_t         count,
85                        uint32_t         chctrlb,
86                        uint16_t         btctrl,
87                        void             (*callback)(uint8_t id, void *closure),
88                        void             *closure)
89 {
90         /* Set up the callback */
91         dmac_callback[id].closure = closure;
92         dmac_callback[id].callback = callback;
93
94         /* Set up the descriptor */
95         samd21_dmac_desc[id].btctrl = btctrl;
96         samd21_dmac_desc[id].btcnt = count;
97         samd21_dmac_desc[id].srcaddr = (uint32_t) src;
98         samd21_dmac_desc[id].dstaddr = (uint32_t) dst;
99         samd21_dmac_desc[id].descaddr = 0;
100
101         /* Configure the channel and enable it */
102         samd21_dmac.chid = id;
103         samd21_dmac.chctrlb = chctrlb;
104         samd21_dmac.chctrla = (1 << SAMD21_DMAC_CHCTRLA_ENABLE);
105 }
106
107 void
108 _ao_dma_done_transfer(uint8_t id)
109 {
110         /* Disable channel */
111         samd21_dmac.chid = id;
112         samd21_dmac.chctrla = 0;
113 }
114
115 void
116 ao_dma_init(void)
117 {
118         uint8_t ch;
119
120         /* Enable DMAC clocks */
121         samd21_pm.ahbmask |= (1 << SAMD21_PM_AHBMASK_DMAC);
122         samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_DMAC);
123
124 #if 0
125         /* Enable HPB clocks so we can talk to peripherals */
126         samd21_pm.ahbmask |= ((1 << SAMD21_PM_AHBMASK_HPB0) |
127                               (1 << SAMD21_PM_AHBMASK_HPB1) |
128                               (1 << SAMD21_PM_AHBMASK_HPB2));
129 #endif
130
131         samd21_pm.apbamask |= (1 << SAMD21_PM_APBAMASK_PAC0);
132         samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_PAC1);
133         samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_PAC2);
134
135         /* Reset DMAC device */
136         samd21_dmac.ctrl = 0;
137         samd21_dmac.ctrl = (1 << SAMD21_DMAC_CTRL_SWRST);
138         while (samd21_dmac.ctrl & (1 << SAMD21_DMAC_CTRL_SWRST))
139                 ;
140
141         samd21_dmac.baseaddr = (uint32_t) &samd21_dmac_desc[0];
142         samd21_dmac.wrbaddr = (uint32_t) &samd21_dmac_wrb[0];
143
144         samd21_dmac.swtrigctrl = 0;
145
146         /* Set QoS to highest value */
147         samd21_dmac.qosctrl = ((SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_DQOS) |
148                                (SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_FQOS) |
149                                (SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_WRBQOS));
150
151         /* Enable DMAC controller with all priority levels */
152         samd21_dmac.ctrl = ((1 << SAMD21_DMAC_CTRL_DMAENABLE) |
153                             (1 << SAMD21_DMAC_CTRL_LVLEN(0)) |
154                             (1 << SAMD21_DMAC_CTRL_LVLEN(1)) |
155                             (1 << SAMD21_DMAC_CTRL_LVLEN(2)) |
156                             (1 << SAMD21_DMAC_CTRL_LVLEN(3)));
157
158         /* Reset all DMAC channels */
159         for (ch = 0; ch < SAMD21_DMAC_NCHAN; ch++) {
160                 samd21_dmac.chid = ch;
161                 samd21_dmac.chctrla = (1 << SAMD21_DMAC_CHCTRLA_SWRST);
162                 while (samd21_dmac.chctrla & (1 << SAMD21_DMAC_CHCTRLA_SWRST))
163                         ;
164                 samd21_dmac.chintenset = (1 << SAMD21_DMAC_CHINTFLAG_TCMPL);
165         }
166
167         /* configure interrupts */
168         samd21_nvic_set_enable(SAMD21_NVIC_ISR_DMAC_POS);
169         samd21_nvic_set_priority(SAMD21_NVIC_ISR_DMAC_POS, 3);
170 }