Skip to content

Commit 2f6ac06

Browse files
Fix spi_init() not freeing DMA channels, causing DMA to get stuck
1 parent 0db79fc commit 2f6ac06

File tree

4 files changed

+72
-43
lines changed

4 files changed

+72
-43
lines changed

hal/include/hal/dma_api.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
* @brief Enumeration of possible DMA usage hints
3131
*/
3232
typedef enum {
33-
DMA_USAGE_NEVER, ///< Never use DMA
33+
DMA_USAGE_NEVER = 0, ///< Never use DMA
3434
DMA_USAGE_OPPORTUNISTIC, ///< Use DMA if possible but deallocate DMA resources when not being used.
3535
DMA_USAGE_ALWAYS, ///< Always use DMA when possible
3636
DMA_USAGE_TEMPORARY_ALLOCATED, // Seems to be used as an internal state indicator for "we need to deallocate these channels."

targets/TARGET_NUVOTON/TARGET_M480/dma_api.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,19 @@ int dma_channel_allocate(uint32_t capabilities)
8484

8585
int dma_channel_free(int channelid)
8686
{
87+
PDMA_T * pdma = dma_modbase();
88+
89+
// Make sure channel is disabled
90+
pdma->CHCTL &= ~(1 << channelid); // Enable this DMA channel
91+
92+
// Also clear the request source for a channel in case still enabled.
93+
// This allows this request source to be assigned to a different channel later.
94+
PDMA_SetTransferMode(pdma,
95+
channelid,
96+
PDMA_MEM, // No peripheral
97+
0, // Scatter-gather disabled
98+
0); // Scatter-gather descriptor address
99+
87100
if (channelid != DMA_ERROR_OUT_OF_CHANNELS) {
88101
dma_chn_mask &= ~(1 << channelid);
89102
}

targets/TARGET_NUVOTON/TARGET_M480/spi_api.c

Lines changed: 56 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,48 @@ static uint8_t nu_spi_get_bytes_per_word(struct spi_s const * const nu_spi)
143143
}
144144
}
145145

146+
// Set the DMA usage of this SPI instance.
147+
// Allocates or deallocates channels as necessary.
148+
// If no DMA channels are available, sets DMA usage to DMA_USAGE_NEVER
149+
static void nu_spi_set_dma_usage(struct spi_s * const spi, DMAUsage new_dma_usage)
150+
{
151+
if(new_dma_usage == DMA_USAGE_NEVER)
152+
{
153+
if(spi->dma_usage != DMA_USAGE_NEVER)
154+
{
155+
// Free channels
156+
dma_channel_free(spi->dma_chn_id_tx);
157+
spi->dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS;
158+
dma_channel_free(spi->dma_chn_id_rx);
159+
spi->dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS;
160+
}
161+
}
162+
else
163+
{
164+
// Temporary or permanent DMA usage
165+
if(spi->dma_usage == DMA_USAGE_NEVER)
166+
{
167+
// Need to allocate channels
168+
spi->dma_chn_id_tx = dma_channel_allocate(DMA_CAP_NONE);
169+
if(spi->dma_chn_id_tx == DMA_ERROR_OUT_OF_CHANNELS)
170+
{
171+
new_dma_usage = DMA_USAGE_NEVER;
172+
}
173+
else
174+
{
175+
spi->dma_chn_id_rx = dma_channel_allocate(DMA_CAP_NONE);
176+
if(spi->dma_chn_id_rx == DMA_ERROR_OUT_OF_CHANNELS)
177+
{
178+
new_dma_usage = DMA_USAGE_NEVER;
179+
dma_channel_free(spi->dma_chn_id_tx);
180+
}
181+
}
182+
}
183+
}
184+
185+
spi->dma_usage = new_dma_usage;
186+
}
187+
146188
#if DEVICE_SPI_ASYNCH
147189
static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable);
148190
static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable);
@@ -259,10 +301,8 @@ void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel
259301
SYS_ResetModule(modinit->rsetidx);
260302

261303
#if DEVICE_SPI_ASYNCH
262-
obj->spi.dma_usage = DMA_USAGE_NEVER;
263-
obj->spi.event = 0;
264-
obj->spi.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS;
265-
obj->spi.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS;
304+
// Note: We don't want to touch the DMA usage here, because either this is a completely new SPI and the DMA usage is already set to 0 (NEVER),
305+
// or it's a re-initialization of an existing SPI and we can allow it to keep its existing DMA settings.
266306

267307
/* NOTE: We use vector to judge if asynchronous transfer is on-going (spi_active).
268308
* At initial time, asynchronous transfer is not on-going and so vector must
@@ -278,14 +318,8 @@ void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel
278318
void spi_free(spi_t *obj)
279319
{
280320
#if DEVICE_SPI_ASYNCH
281-
if (obj->spi.dma_chn_id_tx != DMA_ERROR_OUT_OF_CHANNELS) {
282-
dma_channel_free(obj->spi.dma_chn_id_tx);
283-
obj->spi.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS;
284-
}
285-
if (obj->spi.dma_chn_id_rx != DMA_ERROR_OUT_OF_CHANNELS) {
286-
dma_channel_free(obj->spi.dma_chn_id_rx);
287-
obj->spi.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS;
288-
}
321+
// Free DMA channels
322+
nu_spi_set_dma_usage(&obj->spi, DMA_USAGE_NEVER);
289323
#endif
290324

291325
if (spi_is_qspi(obj)) {
@@ -533,20 +567,17 @@ bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
533567
MBED_ASSERT(tx_length % word_size_bytes == 0);
534568
MBED_ASSERT(rx_length % word_size_bytes == 0);
535569

536-
obj->spi.dma_usage = hint;
537-
spi_check_dma_usage(&obj->spi.dma_usage, &obj->spi.dma_chn_id_tx, &obj->spi.dma_chn_id_rx);
538570
// Conditions to go DMA way:
539571
// (1) No DMA support for non-8 multiple data width.
540572
// (2) tx length >= rx length. Otherwise, as tx DMA is done, no bus activity for remaining rx.
541573
if (((obj->spi.word_size_bits % 8) != 0) ||
542574
(tx_length < rx_length)) {
543-
obj->spi.dma_usage = DMA_USAGE_NEVER;
544-
dma_channel_free(obj->spi.dma_chn_id_tx);
545-
obj->spi.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS;
546-
dma_channel_free(obj->spi.dma_chn_id_rx);
547-
obj->spi.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS;
575+
hint = DMA_USAGE_NEVER;
548576
}
549577

578+
// Set DMA usage, allocating or releasing DMA channels
579+
nu_spi_set_dma_usage(&obj->spi, hint);
580+
550581
// SPI IRQ is necessary for both interrupt way and DMA way
551582
spi_enable_event(obj, event, 1);
552583
spi_buffer_set(obj, tx, tx_length, rx, rx_length);
@@ -689,6 +720,12 @@ void spi_abort_asynch(spi_t *obj)
689720
pdma_base->CHCTL &= ~(1 << obj->spi.dma_chn_id_rx);
690721
}
691722
SPI_DISABLE_RX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi)));
723+
724+
// If DMA was temporary, free its channels
725+
if(obj->spi.dma_usage == DMA_USAGE_TEMPORARY_ALLOCATED || obj->spi.dma_usage == DMA_USAGE_OPPORTUNISTIC)
726+
{
727+
nu_spi_set_dma_usage(&obj->spi, DMA_USAGE_NEVER);
728+
}
692729
}
693730

694731
// Necessary for both interrupt way and DMA way
@@ -927,29 +964,6 @@ static void spi_buffer_set(spi_t *obj, const void *tx, size_t tx_length, void *r
927964
obj->rx_buff.width = nu_spi_get_bytes_per_word(&obj->spi) * 8;
928965
}
929966

930-
static void spi_check_dma_usage(DMAUsage *dma_usage, int *dma_ch_tx, int *dma_ch_rx)
931-
{
932-
if (*dma_usage != DMA_USAGE_NEVER) {
933-
if (*dma_ch_tx == DMA_ERROR_OUT_OF_CHANNELS) {
934-
*dma_ch_tx = dma_channel_allocate(DMA_CAP_NONE);
935-
}
936-
if (*dma_ch_rx == DMA_ERROR_OUT_OF_CHANNELS) {
937-
*dma_ch_rx = dma_channel_allocate(DMA_CAP_NONE);
938-
}
939-
940-
if (*dma_ch_tx == DMA_ERROR_OUT_OF_CHANNELS || *dma_ch_rx == DMA_ERROR_OUT_OF_CHANNELS) {
941-
*dma_usage = DMA_USAGE_NEVER;
942-
}
943-
}
944-
945-
if (*dma_usage == DMA_USAGE_NEVER) {
946-
dma_channel_free(*dma_ch_tx);
947-
*dma_ch_tx = DMA_ERROR_OUT_OF_CHANNELS;
948-
dma_channel_free(*dma_ch_rx);
949-
*dma_ch_rx = DMA_ERROR_OUT_OF_CHANNELS;
950-
}
951-
}
952-
953967
static int spi_is_tx_complete(spi_t *obj)
954968
{
955969
return (obj->tx_buff.pos == obj->tx_buff.length);

targets/TARGET_NUVOTON/nu_bitutil.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,14 @@ __STATIC_INLINE int nu_clo(uint32_t x)
3434
return nu_clz(~x);
3535
}
3636

37+
// Count Trailing Zeroes in an integer
3738
__STATIC_INLINE int nu_ctz(uint32_t x)
3839
{
3940
int c = __CLZ(x & -x);
4041
return x ? 31 - c : c;
4142
}
4243

44+
// Count Trailing Ones in an integer
4345
__STATIC_INLINE int nu_cto(uint32_t x)
4446
{
4547
return nu_ctz(~x);

0 commit comments

Comments
 (0)