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
Signed by: Lephenixnoir
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 *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
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;
/* First additional on-chip memory area (XRAM) */
if(a >= 0xe5007000 && a < 0xE5009000)
if(a >= 0xe5007000 && a < 0xe5009000)
return a;
/* Second on-chip memory area (YRAM) */
@ -123,8 +123,18 @@ void dma_transfer_wait(int channel)
channel_t *ch = dma_channel(channel);
if(!ch) return;
/* Wait for the channel to be disabled by the interrupt handler */
while(ch->CHCR.DE) sleep();
/* Wait for the channel to be disabled by the interrupt handler.
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 */

View File

@ -7,6 +7,7 @@ void *dma_memset(void *dst, uint32_t l, size_t size)
uint32_t *IL = (void *)0xe5200000;
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;
}