2 * Copyright © 2019 Keith Packard <keithp@keithp.com>
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.
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.
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.
20 #include <ao_dma_samd21.h>
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)));
26 void (*callback)(uint8_t id, void *closure);
28 } dmac_callback[SAMD21_DMAC_NCHAN];
33 uint16_t intpend = samd21_dmac.intpend;
35 if (intpend & 0x0700) {
36 uint8_t id = (intpend >> SAMD21_DMAC_INTPEND_ID) & SAMD21_DMAC_INTPEND_ID_MASK;
37 samd21_dmac.intpend = intpend;
38 if (intpend & (1 << SAMD21_DMAC_INTPEND_TCMPL)) {
39 if (dmac_callback[id].callback)
40 (*dmac_callback[id].callback)(id, dmac_callback[id].closure);
46 ao_dma_dump(char *where)
48 printf("DMA %s ctrl %04x intpend %04x intstatus %04lx\n",
52 samd21_dmac.intstatus);
54 printf(" busych %04lx pendch %04lx active %08lx chctrla %02x\n",
60 printf(" chctrlb %08lx chintflag %02x chstatus %02x\n",
62 samd21_dmac.chintflag,
63 samd21_dmac.chstatus);
65 printf(" btctrl %04x btcnt %04x srcaddr %08lx dstaddr %08lx descaddr %08lx\n",
66 samd21_dmac_desc[0].btctrl,
67 samd21_dmac_desc[0].btcnt,
68 samd21_dmac_desc[0].srcaddr,
69 samd21_dmac_desc[0].dstaddr,
70 samd21_dmac_desc[0].descaddr);
75 _ao_dma_start_transfer(uint8_t id,
81 void (*callback)(uint8_t id, void *closure),
84 /* Set up the callback */
85 dmac_callback[id].closure = closure;
86 dmac_callback[id].callback = callback;
88 /* Set up the descriptor */
89 samd21_dmac_desc[id].btctrl = btctrl;
90 samd21_dmac_desc[id].btcnt = count;
91 samd21_dmac_desc[id].srcaddr = (uint32_t) src;
92 samd21_dmac_desc[id].dstaddr = (uint32_t) dst;
93 samd21_dmac_desc[id].descaddr = 0;
95 /* Configure the channel and enable it */
96 samd21_dmac.chid = id;
97 samd21_dmac.chctrlb = chctrlb;
98 samd21_dmac.chctrla = (1 << SAMD21_DMAC_CHCTRLA_ENABLE);
102 _ao_dma_done_transfer(uint8_t id)
104 /* Disable channel */
105 samd21_dmac.chid = id;
106 samd21_dmac.chctrla = 0;
114 /* Enable DMAC clocks */
115 samd21_pm.ahbmask |= (1 << SAMD21_PM_AHBMASK_DMAC);
116 samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_DMAC);
119 /* Enable HPB clocks so we can talk to peripherals */
120 samd21_pm.ahbmask |= ((1 << SAMD21_PM_AHBMASK_HPB0) |
121 (1 << SAMD21_PM_AHBMASK_HPB1) |
122 (1 << SAMD21_PM_AHBMASK_HPB2));
125 samd21_pm.apbamask |= (1 << SAMD21_PM_APBAMASK_PAC0);
126 samd21_pm.apbbmask |= (1 << SAMD21_PM_APBBMASK_PAC1);
127 samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_PAC2);
129 /* Reset DMAC device */
130 samd21_dmac.ctrl = 0;
131 samd21_dmac.ctrl = (1 << SAMD21_DMAC_CTRL_SWRST);
132 while (samd21_dmac.ctrl & (1 << SAMD21_DMAC_CTRL_SWRST))
135 samd21_dmac.baseaddr = (uint32_t) &samd21_dmac_desc[0];
136 samd21_dmac.wrbaddr = (uint32_t) &samd21_dmac_wrb[0];
138 samd21_dmac.swtrigctrl = 0;
140 /* Set QoS to highest value */
141 samd21_dmac.qosctrl = ((SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_DQOS) |
142 (SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_FQOS) |
143 (SAMD21_DMAC_QOSCTRL_HIGH << SAMD21_DMAC_QOSCTRL_WRBQOS));
145 /* Enable DMAC controller with all priority levels */
146 samd21_dmac.ctrl = ((1 << SAMD21_DMAC_CTRL_DMAENABLE) |
147 (1 << SAMD21_DMAC_CTRL_LVLEN(0)) |
148 (1 << SAMD21_DMAC_CTRL_LVLEN(1)) |
149 (1 << SAMD21_DMAC_CTRL_LVLEN(2)) |
150 (1 << SAMD21_DMAC_CTRL_LVLEN(3)));
152 /* Reset all DMAC channels */
153 for (ch = 0; ch < SAMD21_DMAC_NCHAN; ch++) {
154 samd21_dmac.chid = ch;
155 samd21_dmac.chctrla = (1 << SAMD21_DMAC_CHCTRLA_SWRST);
156 while (samd21_dmac.chctrla & (1 << SAMD21_DMAC_CHCTRLA_SWRST))
158 samd21_dmac.chintenset = (1 << SAMD21_DMAC_CHINTFLAG_TCMPL);
161 /* configure interrupts */
162 samd21_nvic_set_enable(SAMD21_NVIC_ISR_DMAC_POS);
163 samd21_nvic_set_priority(SAMD21_NVIC_ISR_DMAC_POS, 3);