mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2025-04-19 01:16:56 +02:00
dma: add support for all six channels (merges #1)
This commit is contained in:
parent
ab0fa06a1d
commit
b3cbb0a43f
5 changed files with 131 additions and 71 deletions
|
@ -47,22 +47,26 @@ typedef enum
|
||||||
dma_transfer_wait() if you need to wait for the transfer to finish. Don't
|
dma_transfer_wait() if you need to wait for the transfer to finish. Don't
|
||||||
start a new transfer until the current one is finished!
|
start a new transfer until the current one is finished!
|
||||||
|
|
||||||
|
@channel DMA channel (0..5)
|
||||||
@size Transfer size
|
@size Transfer size
|
||||||
@blocks Number of blocks (transferred memory = size * blocks)
|
@blocks Number of blocks (transferred memory = size * blocks)
|
||||||
@src Source pointer, must be aligned with transfer size
|
@src Source pointer, must be aligned with transfer size
|
||||||
@src_mode Source address mode
|
@src_mode Source address mode
|
||||||
@dst Destination address, must be aligned with transfer size
|
@dst Destination address, must be aligned with transfer size
|
||||||
@dst_mode Destination address mode */
|
@dst_mode Destination address mode */
|
||||||
void dma_transfer(dma_size_t size, uint length,
|
void dma_transfer(int channel, dma_size_t size, uint length,
|
||||||
void *src, dma_address_t src_mode,
|
void *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 on channel 0 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,
|
||||||
this is not necessary (the only way to know is to look at the DMA registers
|
this is not necessary (the only way to know is to look at the DMA registers
|
||||||
or record interrupts). */
|
or record interrupts).
|
||||||
void dma_transfer_wait(void);
|
|
||||||
|
@channel DMA channel (0..5) */
|
||||||
|
void dma_transfer_wait(int channel);
|
||||||
|
|
||||||
/* dma_transfer_noint() - Perform a data transfer without interrupts
|
/* dma_transfer_noint() - Perform a data transfer without interrupts
|
||||||
This function performs a transfer much like dma_transfer(), but doesn't use
|
This function performs a transfer much like dma_transfer(), but doesn't use
|
||||||
|
@ -71,7 +75,7 @@ void dma_transfer_wait(void);
|
||||||
|
|
||||||
Not using interrupts is a bad design idea for a majority of programs, and is
|
Not using interrupts is a bad design idea for a majority of programs, and is
|
||||||
only ever needed to display panic messages inside exception handlers. */
|
only ever needed to display panic messages inside exception handlers. */
|
||||||
void dma_transfer_noint(dma_size_t size, uint blocks,
|
void dma_transfer_noint(int channel, dma_size_t size, uint blocks,
|
||||||
void *src, dma_address_t src_mode,
|
void *src, dma_address_t src_mode,
|
||||||
void *dst, dma_address_t dst_mode);
|
void *dst, dma_address_t dst_mode);
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
Note that the many settings are only available on channels 0 to 3 (denoted
|
Note that the many settings are only available on channels 0 to 3 (denoted
|
||||||
by [0..3]) or on channels 0 and 1 (denoted by [0,1]).
|
by [0..3]) or on channels 0 and 1 (denoted by [0,1]).
|
||||||
The documentation is apparently wrong about the placement is TS[3:2], the
|
The documentation is apparently wrong about the placement is TS[3:2], the
|
||||||
neighbouring read-only bit must be swapped before TS[3:2]. */
|
neighboring read-only bit must be swapped before TS[3:2]. */
|
||||||
typedef volatile struct
|
typedef volatile struct
|
||||||
{
|
{
|
||||||
uint32_t SAR;
|
uint32_t SAR;
|
||||||
|
|
163
src/dma/dma.c
163
src/dma/dma.c
|
@ -11,41 +11,57 @@
|
||||||
#define INTC SH7305_INTC
|
#define INTC SH7305_INTC
|
||||||
#define POWER SH7305_POWER
|
#define POWER SH7305_POWER
|
||||||
|
|
||||||
|
typedef volatile sh7305_dma_channel_t channel_t;
|
||||||
|
|
||||||
|
/* dma_channel(): returns address of the specified DMA channel */
|
||||||
|
static channel_t *dma_channel(int channel)
|
||||||
|
{
|
||||||
|
channel_t *addr[6] = {
|
||||||
|
&DMA.DMA0, &DMA.DMA1, &DMA.DMA2,
|
||||||
|
&DMA.DMA3, &DMA.DMA4, &DMA.DMA5,
|
||||||
|
};
|
||||||
|
|
||||||
|
return ((uint)channel >= 6 ? NULL : addr[channel]);
|
||||||
|
}
|
||||||
|
|
||||||
//---
|
//---
|
||||||
// Driver interface
|
// Driver interface
|
||||||
//---
|
//---
|
||||||
|
|
||||||
/* dma_setup() - Setup the DMA in interrupt or no-interrupt mode.
|
/* dma_setup(): Setup the DMA in interrupt or no-interrupt mode.
|
||||||
The first parameters are as for dma_transfer() and dma_transfer_noint(). The
|
The first parameters are as for dma_transfer() and dma_transfer_noint(). The
|
||||||
last parameter indicates whether interrupts should be used.
|
last parameter indicates whether interrupts should be used.
|
||||||
Returns non-zero if the DMA is busy or a configuration error occurs. */
|
Returns non-zero if the DMA is busy or a configuration error occurs. */
|
||||||
static int dma_setup(dma_size_t size, uint blocks,
|
static int dma_setup(int channel, dma_size_t size, uint blocks,
|
||||||
void *src, dma_address_t src_mode,
|
void *src, dma_address_t src_mode,
|
||||||
void *dst, dma_address_t dst_mode,
|
void *dst, dma_address_t dst_mode,
|
||||||
int interrupts)
|
int interrupts)
|
||||||
{
|
{
|
||||||
/* Safety guard: only start a transfer if there's not one running */
|
channel_t *ch = dma_channel(channel);
|
||||||
if(DMA.DMA0.CHCR.DE) return 1;
|
if(!ch) return 1;
|
||||||
|
|
||||||
/* Disable DMA0 and disable the master DMA switch */
|
/* Safety guard: only start a transfer if there's not one running */
|
||||||
DMA.DMA0.CHCR.DE = 0;
|
if(ch->CHCR.DE) return 1;
|
||||||
|
|
||||||
|
/* Disable channel and disable the master DMA switch */
|
||||||
|
ch->CHCR.DE = 0;
|
||||||
DMA.OR.DME = 0;
|
DMA.OR.DME = 0;
|
||||||
|
|
||||||
/* Set DMA source and target address */
|
/* Set DMA source and target address */
|
||||||
DMA.DMA0.SAR = (uint32_t)src & 0x1fffffff;
|
ch->SAR = (uint32_t)src & 0x1fffffff;
|
||||||
DMA.DMA0.DAR = (uint32_t)dst & 0x1fffffff;
|
ch->DAR = (uint32_t)dst & 0x1fffffff;
|
||||||
|
|
||||||
/* Set the number of blocks to be transferred */
|
/* Set the number of blocks to be transferred */
|
||||||
DMA.DMA0.TCR = blocks;
|
ch->TCR = blocks;
|
||||||
|
|
||||||
/* Fill in CHCR. Set RS=0100 (auto-request) and the user-provided
|
/* Fill in CHCR. Set RS=0100 (auto-request) and the user-provided
|
||||||
values for TS (transfer size), DM and SM (address modes) */
|
values for TS (transfer size), DM and SM (address modes) */
|
||||||
DMA.DMA0.CHCR.lword = 0x00000400;
|
ch->CHCR.lword = 0x00000400;
|
||||||
DMA.DMA0.CHCR.TS_32 = (size >> 2);
|
ch->CHCR.TS_32 = (size >> 2);
|
||||||
DMA.DMA0.CHCR.TS_10 = (size & 3);
|
ch->CHCR.TS_10 = (size & 3);
|
||||||
DMA.DMA0.CHCR.DM = dst_mode;
|
ch->CHCR.DM = dst_mode;
|
||||||
DMA.DMA0.CHCR.SM = src_mode;
|
ch->CHCR.SM = src_mode;
|
||||||
DMA.DMA0.CHCR.IE = !!interrupts;
|
ch->CHCR.IE = !!interrupts;
|
||||||
|
|
||||||
/* Prepare DMAOR by enabling the master switch and clearing the
|
/* Prepare DMAOR by enabling the master switch and clearing the
|
||||||
blocking flags. */
|
blocking flags. */
|
||||||
|
@ -56,44 +72,50 @@ static int dma_setup(dma_size_t size, uint blocks,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dma_transfer() - Perform a data transfer */
|
/* dma_transfer(): Perform a data transfer */
|
||||||
void dma_transfer(dma_size_t size, uint blocks,
|
void dma_transfer(int channel, dma_size_t size, uint blocks,
|
||||||
void *src, dma_address_t src_mode,
|
void *src, dma_address_t src_mode,
|
||||||
void *dst, dma_address_t dst_mode)
|
void *dst, dma_address_t dst_mode)
|
||||||
{
|
{
|
||||||
if(dma_setup(size, blocks, src, src_mode, dst, dst_mode, 1)) return;
|
if(dma_setup(channel, size, blocks, src, src_mode, dst, dst_mode, 1))
|
||||||
|
return;
|
||||||
|
|
||||||
/* Enable channel 0, starting the DMA transfer. */
|
/* Enable channel, starting the DMA transfer. */
|
||||||
DMA.DMA0.CHCR.DE = 1;
|
channel_t *ch = dma_channel(channel);
|
||||||
|
ch->CHCR.DE = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dma_transfer_wait() - Wait for a transfer on channel 0 to finish */
|
/* dma_transfer_wait(): Wait for a transfer to finish */
|
||||||
void dma_transfer_wait(void)
|
void dma_transfer_wait(int channel)
|
||||||
{
|
{
|
||||||
/* The master switch is cut when the transfer ends */
|
channel_t *ch = dma_channel(channel);
|
||||||
while(DMA.OR.DME) sleep();
|
if(!ch) return;
|
||||||
|
|
||||||
|
/* Wait for the channel to be disabled by the interrupt handler */
|
||||||
|
while(ch->CHCR.DE) sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dma_transfer_noint() - Perform a data transfer without interruptions */
|
/* dma_transfer_noint(): Perform a data transfer without interruptions */
|
||||||
void dma_transfer_noint(dma_size_t size, uint blocks,
|
void dma_transfer_noint(int channel, dma_size_t size, uint blocks,
|
||||||
void *src, dma_address_t src_mode,
|
void *src, dma_address_t src_mode,
|
||||||
void *dst, dma_address_t dst_mode)
|
void *dst, dma_address_t dst_mode)
|
||||||
{
|
{
|
||||||
if(dma_setup(size, blocks, src, src_mode, dst, dst_mode, 0)) return;
|
if(dma_setup(channel, size, blocks, src, src_mode, dst, dst_mode, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
/* Enable channel 0, starting the DMA transfer. */
|
/* Enable channel, starting the DMA transfer. */
|
||||||
DMA.DMA0.CHCR.DE = 1;
|
channel_t *ch = dma_channel(channel);
|
||||||
|
ch->CHCR.DE = 1;
|
||||||
|
|
||||||
/* Actively wait until the transfer is finished */
|
/* Actively wait until the transfer is finished */
|
||||||
while(!DMA.DMA0.CHCR.TE);
|
while(!ch->CHCR.TE);
|
||||||
|
|
||||||
/* Disable the channel and clear the TE flag. Disable the channel first
|
/* Disable the channel and clear the TE flag. Disable the channel first
|
||||||
as clearing the TE flag will allow the transfer to restart */
|
as clearing the TE flag will allow the transfer to restart */
|
||||||
DMA.DMA0.CHCR.DE = 0;
|
ch->CHCR.DE = 0;
|
||||||
DMA.DMA0.CHCR.TE = 0;
|
ch->CHCR.TE = 0;
|
||||||
|
|
||||||
/* Clear the AE and NMIF status flags and cut the master switch */
|
/* Clear the AE and NMIF status flags */
|
||||||
DMA.OR.DME = 0;
|
|
||||||
DMA.OR.AE = 0;
|
DMA.OR.AE = 0;
|
||||||
DMA.OR.NMIF = 0;
|
DMA.OR.NMIF = 0;
|
||||||
}
|
}
|
||||||
|
@ -108,25 +130,53 @@ static void init(void)
|
||||||
if(isSH3()) return;
|
if(isSH3()) return;
|
||||||
|
|
||||||
/* Install the interrupt handler from dma/inth.s */
|
/* Install the interrupt handler from dma/inth.s */
|
||||||
extern void inth_dma_dma0(void);
|
/* TODO: DMA0 error gate is 0xbc0 */
|
||||||
gint_inthandler(0x800, inth_dma_dma0, 32);
|
int codes[] = { 0x800, 0x820, 0x840, 0x860, 0xb80, 0xba0 };
|
||||||
|
extern void inth_dma_te(void);
|
||||||
|
|
||||||
/* Set interrupt priority to 3 */
|
for(int i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
/* Install interrupt handler */
|
||||||
|
void *h = gint_inthandler(codes[i], inth_dma_te, 32);
|
||||||
|
channel_t *ch = dma_channel(i);
|
||||||
|
|
||||||
|
/* Set its CHCR address */
|
||||||
|
*(volatile uint32_t **)(h + 24) = &ch->CHCR.lword;
|
||||||
|
/* Clear the enable flag */
|
||||||
|
ch->CHCR.DE = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set interrupt priority to 3 (IPRE[15..12] for first three channels,
|
||||||
|
IPRF[11..8] for last two and error gate */
|
||||||
gint_intlevel(16, 3);
|
gint_intlevel(16, 3);
|
||||||
|
gint_intlevel(21, 3);
|
||||||
|
|
||||||
/* Unmask the DMA0 interrupt */
|
/* Unmask the channel interrupts */
|
||||||
INTC.MSKCLR->IMR1 = 0x01;
|
/* TODO: DMA0 error gate is IMR5 & 0x40 */
|
||||||
|
INTC.MSKCLR->IMR1 = 0x0f;
|
||||||
|
INTC.MSKCLR->IMR5 = 0x30;
|
||||||
|
|
||||||
|
/* Clear blocking flags and enable the master switch */
|
||||||
|
DMA.OR.AE = 0;
|
||||||
|
DMA.OR.NMIF = 0;
|
||||||
|
DMA.OR.DME = 1;
|
||||||
|
|
||||||
gint[HWDMA] = HW_LOADED;
|
gint[HWDMA] = HW_LOADED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void unload(void)
|
||||||
|
{
|
||||||
|
/* Make sure any DMA transfer is finished before leaving the app */
|
||||||
|
for(int i = 0; i < 6; i++) dma_transfer_wait(i);
|
||||||
|
}
|
||||||
|
|
||||||
//---
|
//---
|
||||||
// Context system for this driver
|
// Context system for this driver
|
||||||
//---
|
//---
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32_t SAR0, DAR0, TCR0, CHCR0;
|
channel_t ch[6];
|
||||||
uint16_t OR;
|
uint16_t OR;
|
||||||
int clock;
|
int clock;
|
||||||
|
|
||||||
|
@ -139,10 +189,14 @@ static void ctx_save(void *buf)
|
||||||
{
|
{
|
||||||
ctx_t *ctx = buf;
|
ctx_t *ctx = buf;
|
||||||
|
|
||||||
ctx->SAR0 = DMA.DMA0.SAR;
|
for(int i = 0; i < 6; i++)
|
||||||
ctx->DAR0 = DMA.DMA0.DAR;
|
{
|
||||||
ctx->TCR0 = DMA.DMA0.TCR;
|
channel_t *ch = dma_channel(i);
|
||||||
ctx->CHCR0 = DMA.DMA0.CHCR.lword;
|
ctx->ch[i].SAR = ch->SAR;
|
||||||
|
ctx->ch[i].DAR = ch->DAR;
|
||||||
|
ctx->ch[i].TCR = ch->TCR;
|
||||||
|
ctx->ch[i].CHCR.lword = ch->CHCR.lword;
|
||||||
|
}
|
||||||
|
|
||||||
ctx->OR = DMA.OR.word;
|
ctx->OR = DMA.OR.word;
|
||||||
|
|
||||||
|
@ -154,10 +208,14 @@ static void ctx_restore(void *buf)
|
||||||
{
|
{
|
||||||
ctx_t *ctx = buf;
|
ctx_t *ctx = buf;
|
||||||
|
|
||||||
DMA.DMA0.SAR = ctx->SAR0;
|
for(int i = 0; i < 6; i++)
|
||||||
DMA.DMA0.DAR = ctx->DAR0;
|
{
|
||||||
DMA.DMA0.TCR = ctx->TCR0;
|
channel_t *ch = dma_channel(i);
|
||||||
DMA.DMA0.CHCR.lword = ctx->CHCR0;
|
ch->SAR = ctx->ch[i].SAR;
|
||||||
|
ch->DAR = ctx->ch[i].DAR;
|
||||||
|
ch->TCR = ctx->ch[i].TCR;
|
||||||
|
ch->CHCR.lword = ctx->ch[i].CHCR.lword;
|
||||||
|
}
|
||||||
|
|
||||||
DMA.OR.word = ctx->OR;
|
DMA.OR.word = ctx->OR;
|
||||||
|
|
||||||
|
@ -169,15 +227,14 @@ static void ctx_restore(void *buf)
|
||||||
// Driver structure definition
|
// Driver structure definition
|
||||||
//---
|
//---
|
||||||
|
|
||||||
gint_driver_t drv_dma = {
|
gint_driver_t drv_dma0 = {
|
||||||
.name = "DMA",
|
.name = "DMA0",
|
||||||
.init = init,
|
.init = init,
|
||||||
/* Make sure any DMA transfer is finished before leaving the app */
|
.unload = unload,
|
||||||
.unload = dma_transfer_wait,
|
|
||||||
.ctx_size = sizeof(ctx_t),
|
.ctx_size = sizeof(ctx_t),
|
||||||
.sys_ctx = &sys_ctx,
|
.sys_ctx = &sys_ctx,
|
||||||
.ctx_save = ctx_save,
|
.ctx_save = ctx_save,
|
||||||
.ctx_restore = ctx_restore,
|
.ctx_restore = ctx_restore,
|
||||||
};
|
};
|
||||||
|
|
||||||
GINT_DECLARE_DRIVER(2, drv_dma);
|
GINT_DECLARE_DRIVER(2, drv_dma0);
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
** An easy one, just clears some flags and marks all transfers as finished.
|
** An easy one, just clears some flags and marks all transfers as finished.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.global _inth_dma_dma0
|
.global _inth_dma_te
|
||||||
.section .gint.blocks, "ax"
|
.section .gint.blocks, "ax"
|
||||||
.align 4
|
.align 4
|
||||||
|
|
||||||
/* DMA TRANSFER ENDED INTERRUPT HANDLER - 28 BYTES */
|
/* DMA TRANSFER ENDED INTERRUPT HANDLER - 32 BYTES */
|
||||||
|
|
||||||
_inth_dma_dma0:
|
_inth_dma_te:
|
||||||
/* Clear the TE flag and DMA Enable in CHCR */
|
/* Clear the TE flag and DMA Enable in CHCR */
|
||||||
mov.l 1f, r1
|
mov.l 1f, r1
|
||||||
mov.l @r1, r0
|
mov.l @r1, r0
|
||||||
|
@ -17,16 +17,15 @@ _inth_dma_dma0:
|
||||||
and r2, r0
|
and r2, r0
|
||||||
mov.l r0, @r1
|
mov.l r0, @r1
|
||||||
|
|
||||||
/* Clear the AE and NMIF flags in OR, and cut the master switch */
|
/* Clear the AE and NMIF flags in OR */
|
||||||
add #0x34, r1
|
mov.l 2f, r1
|
||||||
mov.w @r1, r0
|
mov.w @r1, r0
|
||||||
shlr8 r0
|
mov #-7, r2
|
||||||
shll8 r0
|
and r2, r0
|
||||||
mov.w r0, @r1
|
mov.w r0, @r1
|
||||||
|
|
||||||
rte
|
rte
|
||||||
nop
|
nop
|
||||||
nop
|
|
||||||
nop
|
|
||||||
|
|
||||||
1: .long 0xfe00802c /* CHCR0 - OR is 0x34 bytes after this */
|
1: .long 0 /* CHCR, set dynamically */
|
||||||
|
2: .long 0xfe008060 /* DMA.OR */
|
||||||
|
|
|
@ -226,12 +226,12 @@ void r61524_display(uint16_t *vram, int start, int height, int interrupts)
|
||||||
if(interrupts)
|
if(interrupts)
|
||||||
{
|
{
|
||||||
/* Usa a normal, interrupt-based transfer */
|
/* Usa a normal, interrupt-based transfer */
|
||||||
dma_transfer(DMA_32B, blocks, src, DMA_INC, dst, DMA_FIXED);
|
dma_transfer(0, DMA_32B, blocks, src, DMA_INC, dst, DMA_FIXED);
|
||||||
/* Wait for it to finish */
|
/* Wait for it to finish */
|
||||||
/* TODO: Allow r61524_display() to return early */
|
/* TODO: Allow r61524_display() to return early */
|
||||||
dma_transfer_wait();
|
dma_transfer_wait(0);
|
||||||
}
|
}
|
||||||
else dma_transfer_noint(DMA_32B, blocks, src, DMA_INC, dst, DMA_FIXED);
|
else dma_transfer_noint(0, DMA_32B, blocks, src,DMA_INC,dst,DMA_FIXED);
|
||||||
}
|
}
|
||||||
|
|
||||||
//---
|
//---
|
||||||
|
|
Loading…
Add table
Reference in a new issue