Skip to content

Commit b89811f

Browse files
authored
Merge pull request #29 from jepler/samd51-dma-hang
samd51: Work around DMA hang
2 parents 4c0deec + 5ca3a8a commit b89811f

File tree

1 file changed

+30
-15
lines changed

1 file changed

+30
-15
lines changed

samd/dma.c

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ static int32_t shared_dma_transfer(void* peripheral,
110110
} else {
111111
#endif
112112

113-
// sercom index is incorrect for SAMD51
114113
dma_configure(SHARED_TX_CHANNEL, sercom_index(peripheral) * 2 + FIRST_SERCOM_TX_TRIGSRC, false);
115114
tx_active = true;
116115
if (buffer_in != NULL) {
@@ -155,8 +154,6 @@ static int32_t shared_dma_transfer(void* peripheral,
155154
if (sercom) {
156155
SercomSpi *s = &((Sercom*) peripheral)->SPI;
157156
s->INTFLAG.reg = SERCOM_SPI_INTFLAG_RXC | SERCOM_SPI_INTFLAG_DRE;
158-
} else {
159-
//QSPI->INTFLAG.reg = QSPI_INTFLAG_RXC | QSPI_INTFLAG_DRE;
160157
}
161158
// Start the RX job first so we don't miss the first byte. The TX job clocks
162159
// the output.
@@ -168,23 +165,41 @@ static int32_t shared_dma_transfer(void* peripheral,
168165
}
169166

170167

171-
if (sercom) {
172-
//DMAC->SWTRIGCTRL.reg |= (1 << SHARED_TX_CHANNEL);
173-
} else {
174-
// Do a manual copy to trigger then DMA. We do 32-bit accesses to match the DMA.
175-
#pragma GCC diagnostic push
176-
#pragma GCC diagnostic ignored "-Wcast-align"
168+
if (!sercom) {
177169
if (rx_active) {
178-
//buffer_in[0] = *src;
179170
DMAC->SWTRIGCTRL.reg |= (1 << SHARED_RX_CHANNEL);
180-
} else {
181-
//*(uint32_t*)dest = ((uint32_t*) buffer_out)[0];
182171
}
183-
#pragma GCC diagnostic pop
184172
}
185173

186-
// Channels cycle between Suspend -> Pending -> Busy and back while transfering. So, we check
187-
// the channels transfer status for an error or completion.
174+
#ifdef SAMD51
175+
// Sometimes (silicon bug?) this DMA transfer never starts, and another channel sits with
176+
// CHSTATUS.reg = 0x3 (BUSY | PENDING). On the other hand, this is a
177+
// legitimate state for a DMA channel to be in (apparently), so we can't use that alone as a check.
178+
// Instead, let's look at the ACTIVE flag. When DMA is hung, everything in ACTIVE is zeros.
179+
bool is_okay = false;
180+
for (int i=0; i<10 && !is_okay; i++) {
181+
bool complete = true;
182+
if (rx_active) {
183+
if (DMAC->Channel[SHARED_RX_CHANNEL].CHSTATUS.reg & 0x3)
184+
complete = false;
185+
}
186+
if (tx_active) {
187+
if (DMAC->Channel[SHARED_TX_CHANNEL].CHSTATUS.reg & 0x3)
188+
complete = false;
189+
}
190+
is_okay = is_okay || (DMAC->ACTIVE.bit.ABUSY || complete);
191+
}
192+
if (!is_okay) {
193+
for (int i=0; i<AUDIO_DMA_CHANNEL_COUNT; i++) {
194+
if(DMAC->Channel[i].CHCTRLA.bit.ENABLE) {
195+
DMAC->Channel[i].CHCTRLA.bit.ENABLE = 0;
196+
DMAC->Channel[i].CHCTRLA.bit.ENABLE = 1;
197+
}
198+
}
199+
}
200+
#endif
201+
202+
// busy-wait for the RX and TX DMAs to either complete or encounter an error
188203
if (rx_active) {
189204
while ((dma_transfer_status(SHARED_RX_CHANNEL) & 0x3) == 0) {}
190205
}

0 commit comments

Comments
 (0)