dma: fix freezes when transferring to/from IL memory

The IL memory is unavailable when the processor goes to sleep, causing
any involved DMA transfer to stall. The dma_transfer_wait() normally
sleeps to save battery power, but this causes the whole system to freeze
and never wake up.

This change lets dma_transfer_wait() decide dynamically whether to sleep
or spinlock. There is no concrete improvement over dma_transfer_noint()
when using IL memory, but it makes dma_transfer() fully generic.

Obviously the same goes for X and Y memory.
This commit is contained in:
Lephe 2019-09-15 15:09:32 +02:00
parent 552b9b9a43
commit bb77e4588d
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
3 changed files with 16 additions and 5 deletions

View file

@ -57,7 +57,7 @@ void dma_transfer(int channel, dma_size_t size, uint length,
void const *src, dma_address_t src_mode, void const *src, dma_address_t src_mode,
void *dst, dma_address_t dst_mode); void *dst, dma_address_t dst_mode);
/* dma_transfer_wait() - Wait for a transfer on channel 0 to finish /* dma_transfer_wait() - Wait for a transfer to finish
You should call this function when you need to transfer to be complete You should call this function when you need to transfer to be complete
before continuing execution. If you are sure that the transfer is finished, before continuing execution. If you are sure that the transfer is finished,

View file

@ -41,7 +41,7 @@ static uint32_t dma_translate(void const *address)
return a; return a;
/* First additional on-chip memory area (XRAM) */ /* First additional on-chip memory area (XRAM) */
if(a >= 0xe5007000 && a < 0xE5009000) if(a >= 0xe5007000 && a < 0xe5009000)
return a; return a;
/* Second on-chip memory area (YRAM) */ /* Second on-chip memory area (YRAM) */
@ -123,8 +123,18 @@ void dma_transfer_wait(int channel)
channel_t *ch = dma_channel(channel); channel_t *ch = dma_channel(channel);
if(!ch) return; if(!ch) return;
/* Wait for the channel to be disabled by the interrupt handler */ /* Wait for the channel to be disabled by the interrupt handler.
while(ch->CHCR.DE) sleep(); When the source or the destination of the transfer is X, Y or IL
memory, refrain from sleeping as this also stops the transfer. */
int onchip = 0;
if(ch->SAR >= 0xe5007000 && ch->SAR < 0xe5204000) onchip = 1;
if(ch->DAR >= 0xe5007000 && ch->DAR < 0xe5204000) onchip = 1;
while(ch->CHCR.DE)
{
if(!onchip) sleep();
}
} }
/* dma_transfer_noint(): Perform a data transfer without interruptions */ /* dma_transfer_noint(): Perform a data transfer without interruptions */

View file

@ -7,6 +7,7 @@ void *dma_memset(void *dst, uint32_t l, size_t size)
uint32_t *IL = (void *)0xe5200000; uint32_t *IL = (void *)0xe5200000;
for(int i = 0; i < 8; i++) IL[i] = l; for(int i = 0; i < 8; i++) IL[i] = l;
dma_transfer_noint(1, DMA_32B, size >> 5, IL, DMA_FIXED, dst, DMA_INC); dma_transfer(1, DMA_32B, size >> 5, IL, DMA_FIXED, dst, DMA_INC);
dma_transfer_wait(1);
return dst; return dst;
} }