From b8abc2eae74cf279d630ef9f8bf1e7dbee10aafd Mon Sep 17 00:00:00 2001 From: Yann MAGNIN Date: Wed, 17 Aug 2022 13:23:28 +0200 Subject: [PATCH] VxKernel 0.6.0-19 : Add DMA driver @add <> board/fxcg50/board | add DMA module support <> src/driver/mpu/sh/sh7305/dma | add async transfer primitives | add DMA memcpy algorithm | add DMA memset algorithm | add DMA wait primitives @update <> vhex/dma | [types] change dma_id_t type | [interface] add dma_spinwait primitives <> vhex/driver | add DMA flags for driver interface <> vhex/driver/mpu/sh/sh7305/dma | expose kernel-level primitives | add internal types definitions <> src/driver/mpu/sh/sh7305/dma | proper channel cache | proper channel interrupt handler | proper channel error handler | proper address translation | proper driver installation (context save/restore) | proper driver interface exposition @fix <> vhex/keyboard/keycode | fix typedef again <> src/dma | fix driver exposition | fix driver interface link dump <> src/driver/mpu/sh/sh7305/intc | fix generic handler installation --- board/fxcg50/board.toml | 1 + include/vhex/dma/interface.h | 1 + include/vhex/dma/types.h | 2 +- include/vhex/driver.h | 2 +- include/vhex/driver/mpu/sh/sh7305/dma.h | 75 +++++ include/vhex/keyboard/keycode.h | 4 +- include/vhex/keyboard/types.h | 4 +- src/dma/dma.c | 4 +- src/driver/mpu/sh/sh7305/dma/dma.c | 349 +++++++++++++++++++++--- src/driver/mpu/sh/sh7305/dma/memcpy.c | 76 +++++- src/driver/mpu/sh/sh7305/dma/memset.c | 63 ++++- src/driver/mpu/sh/sh7305/dma/wait.c | 40 +++ src/driver/mpu/sh/sh7305/intc/intc.c | 13 +- 13 files changed, 573 insertions(+), 61 deletions(-) create mode 100644 src/driver/mpu/sh/sh7305/dma/wait.c diff --git a/board/fxcg50/board.toml b/board/fxcg50/board.toml index 977f770..a266f66 100644 --- a/board/fxcg50/board.toml +++ b/board/fxcg50/board.toml @@ -11,6 +11,7 @@ modules = [ 'kmalloc', 'timer', 'rtc', + 'dma' ] [drivers] diff --git a/include/vhex/dma/interface.h b/include/vhex/dma/interface.h index 68fe8fc..239c0b3 100644 --- a/include/vhex/dma/interface.h +++ b/include/vhex/dma/interface.h @@ -8,6 +8,7 @@ struct dma_drv_interface dma_id_t (*dma_memcpy)(void * restrict, void * restrict, size_t); dma_id_t (*dma_memset)(void * restrict, int, size_t); int (*dma_wait)(dma_id_t); + int (*dma_spinwait)(dma_id_t); }; #endif /* __VHEX_DMA_INTERFACE__ */ diff --git a/include/vhex/dma/types.h b/include/vhex/dma/types.h index e853fe0..7f08b19 100644 --- a/include/vhex/dma/types.h +++ b/include/vhex/dma/types.h @@ -3,6 +3,6 @@ #include -typedef uint32_t dma_id_t; +typedef int32_t dma_id_t; #endif /* __VHEX_DMA_TYPES__ */ diff --git a/include/vhex/driver.h b/include/vhex/driver.h index fdce071..1bf964c 100644 --- a/include/vhex/driver.h +++ b/include/vhex/driver.h @@ -48,7 +48,7 @@ struct vhex_driver uint8_t TIMER :1; uint8_t DISPLAY :1; - uint8_t :1; + uint8_t DMA :1; uint8_t :1; uint8_t SHARED :1; uint8_t UNUSED :1; diff --git a/include/vhex/driver/mpu/sh/sh7305/dma.h b/include/vhex/driver/mpu/sh/sh7305/dma.h index 65b09e0..9ee8912 100644 --- a/include/vhex/driver/mpu/sh/sh7305/dma.h +++ b/include/vhex/driver/mpu/sh/sh7305/dma.h @@ -4,6 +4,8 @@ #include #include +#include + //--- // SH7305 Direct Memory Access Controller. Refer to: // "Renesas SH7724 User's Manual: Hardware" @@ -94,7 +96,80 @@ struct __sh7305_dma #define SH7305_DMA (*((volatile struct __sh7305_dma *)0xfe008020)) +//--- +// Kernel-level API +//--- +/* dma_size_t - Transfer block size */ +typedef enum +{ + /* Normal transfers of 1, 2, 4, 8, 16 or 32 bytes at a time */ + DMA_BLOCK_SIZE_1B = 0, + DMA_BLOCK_SIZE_2B = 1, + DMA_BLOCK_SIZE_4B = 2, + DMA_BLOCK_SIZE_8B = 7, + DMA_BLOCK_SIZE_16B = 3, + DMA_BLOCK_SIZE_32B = 4, + /* Transfers of 16 or 32 bytes divided in two operations */ + DMA_BLOCK_SIZE_16B_DIV = 11, + DMA_BLOCK_SIZE_32B_DIV = 12, + +} dma_block_size_t; + +/* dma_addr_op_t - Addressing mode for source and destination regions */ +typedef enum +{ + /* Fixed address mode: the same address is read/written at each step */ + DMA_ADDR_FIXED = 0, + /* Increment: address is incremented by transfer size at each step */ + DMA_ADDR_INC = 1, + /* Decrement: only allowed for 1/2/4-byte transfers */ + DMA_ADDR_DEC = 2, + /* Fixed division mode: address is fixed even in 16/32-divide mode */ + DMA_ADDR_FIXEDDIV = 3, + +} dma_addr_op_t; + +/* sh7305_dma_id_t : internal DMA ID structure */ +typedef union { + uint32_t lword; + struct { + uint32_t INVALID : 1; + uint32_t IDX : 3; + uint32_t KEYFRAME : 28; + }; +} sh7305_dma_id_t; + +/* internal cache channel information */ +typedef struct { + sh7305_dma_id_t id; + volatile struct __sh7305_dma_channel *hw; +} sh7305_dma_channel_t; + +extern dma_id_t sh7305_dma_transfert_async( + uintptr_t src, dma_addr_op_t src_op, + uintptr_t dst, dma_addr_op_t dst_op, + dma_block_size_t block_size, + int block_count +); + +/* dma_memcpy() : memcpy using DMA */ +extern dma_id_t sh7305_dma_memcpy(void * restrict, void * restrict, size_t); + +/* dma_memset() : memset using the DMA */ +extern dma_id_t sh7305_dma_memset(void *dst, int c, size_t sz); + +/* dma_wait() : wait the end of the DMA channel transfer */ +extern int sh7305_dma_wait(dma_id_t id); + +/* dma_wait() : wait the end of the DMA channel transfer without interruption */ +extern int sh7305_dma_spinwait(dma_id_t id); + +/* sh7305_dma_wait() : wait DAM channel */ +extern int sh7305_dma_channel_wait(sh7305_dma_channel_t *ch, int atomic); + +/* sh7305_dma_channel_find() : find a channel using the ID */ +extern sh7305_dma_channel_t *sh7305_dma_channel_find(sh7305_dma_id_t id); #endif /* __VHEX_DRIVER_MPU_SH_SH7305_DMA__ */ diff --git a/include/vhex/keyboard/keycode.h b/include/vhex/keyboard/keycode.h index cb61fd3..716d503 100644 --- a/include/vhex/keyboard/keycode.h +++ b/include/vhex/keyboard/keycode.h @@ -6,7 +6,7 @@ (((row & 0x0f) << 4) | ((column & 0x0f) << 0)) /* Define all keycode */ -enum +typedef enum { KEY_F1 = 0x41, KEY_F2 = 0x42, @@ -68,6 +68,6 @@ enum KEY_UNUSED = 0xff, KEY_NONE = 0xfe, -}; +} key_t; #endif /* __VHEX_KEYBOARD_KEYCODE__ */ diff --git a/include/vhex/keyboard/types.h b/include/vhex/keyboard/types.h index 604e5a3..f6d4e89 100644 --- a/include/vhex/keyboard/types.h +++ b/include/vhex/keyboard/types.h @@ -4,6 +4,8 @@ #include #include +#include + /* key_event_t: Low-level or high-level keyboard event This structure represents an event that occurs on the keyboard. It is first @@ -54,7 +56,5 @@ enum KEYEV_REP_NEXT = 2, }; -/* keyboard key */ -typedef int key_t; #endif /* __VHEX_KEYBOARD_TYPES__ */ diff --git a/src/dma/dma.c b/src/dma/dma.c index 740788e..b6d76c7 100644 --- a/src/dma/dma.c +++ b/src/dma/dma.c @@ -19,7 +19,7 @@ static void __dma_init(void) for (int i = 0; i < vhex_driver_count(); ++i) { if (driver[i].flags.DMA) { memcpy( - &rtc_info.driver, + &dma_info.driver, driver[i].module_data, sizeof(struct dma_drv_interface) ); @@ -36,7 +36,7 @@ static void __dma_quit(void) /* declare the timer module */ -struct vhex_module mod_rtc = { +struct vhex_module mod_dma = { .name = "DMA", .init = &__dma_init, .quit = &__dma_quit, diff --git a/src/driver/mpu/sh/sh7305/dma/dma.c b/src/driver/mpu/sh/sh7305/dma/dma.c index 0f811ea..12dbe8d 100644 --- a/src/driver/mpu/sh/sh7305/dma/dma.c +++ b/src/driver/mpu/sh/sh7305/dma/dma.c @@ -1,31 +1,253 @@ #include #include +#include //#include #include #include +#include + +#include + + +/* internal DMA cache information */ +static struct { + uint32_t keyframe; + sh7305_dma_channel_t channel[6]; +} dmac_info = { + .keyframe = 0x00000000, + .channel = { + { + .id = { + .INVALID = 1, + .IDX = 0b111, + .KEYFRAME = 0 + }, + .hw = &SH7305_DMA.DMA0 + }, + { + .id = { + .INVALID = 1, + .IDX = 0b111, + .KEYFRAME = 0 + }, + .hw = &SH7305_DMA.DMA1 + }, + { + .id = { + .INVALID = 1, + .IDX = 0b111, + .KEYFRAME = 0 + }, + .hw = &SH7305_DMA.DMA2 + }, + { + .id = { + .INVALID = 1, + .IDX = 0b111, + .KEYFRAME = 0 + }, + .hw = &SH7305_DMA.DMA3 + }, + { + .id = { + .INVALID = 1, + .IDX = 0b111, + .KEYFRAME = 0 + }, + .hw = &SH7305_DMA.DMA4 + }, + { + .id = { + .INVALID = 1, + .IDX = 0b111, + .KEYFRAME = 0 + }, + .hw = &SH7305_DMA.DMA5 + }, + } +}; + +//--- +// Driver internals +//--- + +/* dma_translate(): Translate virtual address to DMA-suitable form */ +static uintptr_t sh7305_dma_translate_addr(uintptr_t a) +{ + /* Preserve RS addresses (as of SH7724 Reference, 11.2.2) */ + if(a >= 0xfd800000 && a < 0xfd800800) + return a; + + /* Translate virtual addresses to IL memory to physical addresses; the + same address is used (as of SH7724 Reference, 10.3.3) */ + if(a >= 0xe5200000 && a < 0xe5204000) + return a; + + /* First additional on-chip memory area (XRAM) */ + if(a >= 0xe5007000 && a < 0xe5009000) + return a; + + /* Second on-chip memory area (YRAM) */ + if(a >= 0xe5017000 && a < 0xe5019000) + return a; + + /* Translate P1 and P2 addresses to ROM and RAM to physical form */ + if(a >= 0x80000000 && a < 0xc0000000) + return a & 0x1fffffff; + + /* By default: I don't know what this is, let's preserve it */ + return a; +} + +/* sh7305_dma_channel_reserve() : wait until a channel is free and reserve it */ +static sh7305_dma_channel_t *sh7305_dma_channel_reserve(void) +{ + sh7305_dma_channel_t *ch; + + ch = NULL; + while (1) + { + cpu_atomic_start(); + for (int i = 5; i >= 0; --i) { + if (dmac_info.channel[i].id.INVALID == 0) + continue; + dmac_info.keyframe += 1; + ch = &dmac_info.channel[i]; + ch->id.INVALID = 0; + ch->id.IDX = i; + ch->id.KEYFRAME = dmac_info.keyframe; + break; + } + cpu_atomic_end(); + + if (ch != NULL) + break; + + __asm__ volatile ("sleep"); + } + + return ch; +} + +/* sh7305_dma_channel_release() : release DMA channel */ +static int sh7305_dma_channel_release(sh7305_dma_channel_t * ch, int atomic) +{ + if (sh7305_dma_channel_wait(ch, atomic) != 0) + return -1; + + ch->id.INVALID = 1; + return 0; +} + +/* sh7305_dma_channel_find() : find a channel using the ID */ +sh7305_dma_channel_t *sh7305_dma_channel_find(sh7305_dma_id_t id) +{ + if (id.INVALID == 1) + return NULL; + if (id.IDX < 0 && id.IDX >= 6) + return NULL; + return &dmac_info.channel[id.IDX]; +} + +/* sh7305_dma_wait() : wait DAM channel */ +int sh7305_dma_channel_wait(sh7305_dma_channel_t *ch, int atomic) +{ + int find; + + find = -1; + + cpu_atomic_start(); + for (int i = 0; i < 6; ++i) { + if (ch != &dmac_info.channel[i]) + continue; + find = 0; + break; + } + cpu_atomic_end(); + + if (find != 0) + return find; + + while (1) { + if (ch->hw->CHCR.DE == 0) + break; + if (atomic == 1) + __asm__ volatile ("sleep"); + } + + return 0; +} -#define DMA SH7305_DMA -//#define POWER SH7305_POWER -typedef volatile struct __sh7305_dma_channel channel_t; //--- // Driver public API //--- -static channel_t *sh7305_dma_channel(int channel) -{ - if (channel < 0 || channel >= 6) - return NULL; - return (channel_t *[6]){ - &DMA.DMA0, &DMA.DMA1, &DMA.DMA2, - &DMA.DMA3, &DMA.DMA4, &DMA.DMA5, - }[channel]; -} +/* sh7305_dma_transfert_async() : start asynchronous transfert */ +dma_id_t sh7305_dma_transfert_async( + uintptr_t src, dma_addr_op_t src_op, + uintptr_t dst, dma_addr_op_t dst_op, + dma_block_size_t block_size, + int block_count +) { + sh7305_dma_channel_t *ch; -static void sh7305_dma_spinwait(channel_t *ch) -{ - (void)ch; + ch = sh7305_dma_channel_reserve(); + if (ch == NULL) + return -1; + + /* disable channel */ + ch->hw->CHCR.DE = 0; + + /* set source / destination address and block counter */ + ch->hw->SAR = sh7305_dma_translate_addr(src); + ch->hw->DAR = sh7305_dma_translate_addr(dst); + ch->hw->TCR = block_count & 0x0fffffff; + + /* common channel configuration + <> enable release of the bus mastership between reading and writting + <> select normal mode (channel 0..3) + <> select asynchronous DREQ sampling (channel 0) + <> detect overrun 0 (channel 0) + <> clear half-end flags + <> disable half-end interruption + <> select DACK output at read cycle (channel 0) + <> select low-active output of DACK (channel 0) + <> set the approriate destination addressing mode + <> set the approriate source addressing mode + <> select auto-request transfert + <> select DREQ detection on low-level (channel 0) + <> select cycle steal mode + <> enable interruption + <> clear interrupt flags */ + ch->hw->CHCR.LCKN = 1; + ch->hw->CHCR.RPT = 0b000; + ch->hw->CHCR.DA = 0; + ch->hw->CHCR.DO = 0; + ch->hw->CHCR.HE = 0; + ch->hw->CHCR.HIE = 0; + ch->hw->CHCR.AM = 0; + ch->hw->CHCR.AL = 0; + ch->hw->CHCR.DM = dst_op; + ch->hw->CHCR.SM = src_op; + ch->hw->CHCR.RS = 0b0100; + ch->hw->CHCR.DL = 0; + ch->hw->CHCR.DS = 0; + ch->hw->CHCR.TB = 0; + ch->hw->CHCR.IE = 1; + ch->hw->CHCR.TE = 0; + + /* set the block size */ + ch->hw->CHCR.TS3 = (block_size & 0b1000) >> 3; + ch->hw->CHCR.TS2 = (block_size & 0b0100) >> 2; + ch->hw->CHCR.TS1 = (block_size & 0b0010) >> 1; + ch->hw->CHCR.TS0 = (block_size & 0b0001) >> 0; + + /* start DMA channel */ + ch->hw->CHCR.DE = 1; + + return ch->id.lword; } //--- @@ -33,9 +255,43 @@ static void sh7305_dma_spinwait(channel_t *ch) //--- /* Interrupt handler for all finished DMA transfers */ -static void sh7305_dma_interrupt_transfer_ended(channel_t *ch) +static void sh7305_dma_interrupt_transfer_ended(sh7305_dma_channel_t *ch) { - (void)ch; + ch->hw->CHCR.IE = 0; + ch->hw->CHCR.DE = 0; + ch->hw->CHCR.TE = 0; + + SH7305_DMA.DMAOR.AE = 0; + SH7305_DMA.DMAOR.NMIF = 0; + + ch->id.INVALID = 1; +} + +/* DMA error handler (interrupt) */ +static void sh7305_dma_error_handler(void) +{ + dclear(C_WHITE); + dprint(0, 0, C_BLACK, + "DMA error handler !!!\n" + " DMA0.CHCR = %p\n" + " DMA1.CHCR = %p\n" + " DMA2.CHCR = %p\n" + " DMA3.CHCR = %p\n" + " DMA4.CHCR = %p\n" + " DMA5.CHCR = %p\n" + " DMAOR = %p\n" + "reset the device..." + , + SH7305_DMA.DMA0.CHCR.lword, + SH7305_DMA.DMA1.CHCR.lword, + SH7305_DMA.DMA2.CHCR.lword, + SH7305_DMA.DMA3.CHCR.lword, + SH7305_DMA.DMA4.CHCR.lword, + SH7305_DMA.DMA5.CHCR.lword, + SH7305_DMA.DMAOR.word + ); + dupdate(); + while (1) { __asm__ volatile ("sleep"); } } //--- @@ -44,7 +300,7 @@ static void sh7305_dma_interrupt_transfer_ended(channel_t *ch) //FIXME: avoid manualy sync DMAOR with header o(x_x)o struct dma_ctx { - channel_t ch[6]; + volatile struct __sh7305_dma_channel ch[6]; word_union(DMAOR, uint16_t CMS :4; /* Cycle steal Mode Select */ @@ -73,8 +329,8 @@ static void __dma_configure(struct dma_ctx *s) sh7305_intc_install_inth_generic( codes[i], VHEX_CALL( - sh7305_dma_interrupt_transfer_ended, - (void*)sh7305_dma_channel(i) + &sh7305_dma_interrupt_transfer_ended, + (void*)&dmac_info.channel[i] ) ); @@ -83,22 +339,27 @@ static void __dma_configure(struct dma_ctx *s) s->ch[i].TCR = 0x00000000; s->ch[i].CHCR.DE = 0; + + dmac_info.channel[i].id.INVALID = 1; } /* Configure the DMA operations <> select normal cycle steal mode - <> select "normal" priority (CH0->CH1->CH2->...) + <> select round-robin mode <> clear address error flags <> clear NMIF flags <> enable the master switch */ s->DMAOR.CMS = 0b0000; - s->DMAOR.PR = 0b00; + s->DMAOR.PR = 0b11; s->DMAOR.AE = 0; s->DMAOR.NMIF = 0; s->DMAOR.DME = 1; /* Install Address Error interrupt gate */ - sh7305_intc_install_inth_gate(0xbc0, sh7305_dma_inth_ae, 32); + sh7305_intc_install_inth_generic( + 0xbc0, + VHEX_CALL(&sh7305_dma_error_handler) + ); /* Set interrupt priority to 3, except for the channels that are used by the USB driver (channel 0) */ @@ -114,37 +375,40 @@ static void __dma_configure(struct dma_ctx *s) /* __rtc_hsave() : save hardware information */ static void __dma_hsave(struct dma_ctx *s) { + sh7305_dma_channel_t * ch; + for(int i = 0; i < 6; i++) { - channel_t *ch = sh7305_dma_channel(i); - sh7305_dma_spinwait(ch); - ch->SAR = s->ch[i].SAR; - ch->DAR = s->ch[i].DAR; - ch->TCR = s->ch[i].TCR; - ch->CHCR.lword = s->ch[i].CHCR.lword; + ch = &dmac_info.channel[i]; + sh7305_dma_channel_wait(ch, 1); + s->ch[i].SAR = ch->hw->SAR; + s->ch[i].DAR = ch->hw->DAR; + s->ch[i].TCR = ch->hw->TCR; + s->ch[i].CHCR.lword = ch->hw->CHCR.lword; } - DMA.DMAOR.word = s->DMAOR.word; + s->DMAOR.word = SH7305_DMA.DMAOR.word; } /* __dma_hrestore() : restore hardware information */ static void __dma_hrestore(struct dma_ctx *s) { - DMA.DMAOR.DME = 0; + sh7305_dma_channel_t * ch; + + SH7305_DMA.DMAOR.DME = 0; for(int i = 0; i < 6; i++) { - channel_t *ch = sh7305_dma_channel(i); - ch->SAR = s->ch[i].SAR; - ch->DAR = s->ch[i].DAR; - ch->TCR = s->ch[i].TCR; - ch->CHCR.lword = s->ch[i].CHCR.lword; + ch = &dmac_info.channel[i]; + ch->hw->SAR = s->ch[i].SAR; + ch->hw->DAR = s->ch[i].DAR; + ch->hw->TCR = s->ch[i].TCR; + ch->hw->CHCR.lword = s->ch[i].CHCR.lword; } - DMA.DMAOR.word = s->DMAOR.word; + SH7305_DMA.DMAOR.word = s->DMAOR.word; } -#if 0 -struct vhex_driver drv_rtc = { +struct vhex_driver drv_dma = { .name = "DMA", .hsave = (void*)&__dma_hsave, .hrestore = (void*)&__dma_hrestore, @@ -155,6 +419,11 @@ struct vhex_driver drv_rtc = { .SHARED = 0, .UNUSED = 0, }, + .module_data = &(struct dma_drv_interface){ + .dma_memcpy = &sh7305_dma_memcpy, + .dma_memset = &sh7305_dma_memset, + .dma_wait = &sh7305_dma_wait, + .dma_spinwait = &sh7305_dma_spinwait + } }; VHEX_DECLARE_DRIVER(05, drv_dma); -#endif diff --git a/src/driver/mpu/sh/sh7305/dma/memcpy.c b/src/driver/mpu/sh/sh7305/dma/memcpy.c index 93f2cec..f1d378f 100644 --- a/src/driver/mpu/sh/sh7305/dma/memcpy.c +++ b/src/driver/mpu/sh/sh7305/dma/memcpy.c @@ -1,9 +1,79 @@ +#include #include +#include + /* dma_memcpy() : memcpy using DMA */ dma_id_t sh7305_dma_memcpy(void * restrict dst, void * restrict src, size_t sz) { - (void)dst; - (void)src; - (void)sz; + uintptr_t _dst; + uintptr_t _src; + uintptr_t dma_src; + uintptr_t dma_dst; + ptrdiff_t delta; + int dma_block_count; + dma_block_size_t dma_block_size; + int divisor; + ptrdiff_t gap_size_pre; + ptrdiff_t gap_size_end; + dma_id_t id; + + if (sz < 64) { + memcpy(dst, src, sz); + return 0; + } + + _dst = (uintptr_t)dst; + _src = (uintptr_t)src; + + delta = (dst > src) ? dst - src : src - dst; + if ((delta & 0x0000001f) == 0) { + dma_block_size = DMA_BLOCK_SIZE_32B; + divisor = 32; + } else if ((delta & 0x0000000f) == 0) { + dma_block_size = DMA_BLOCK_SIZE_16B; + divisor = 16; + } else if ((delta & 0x00000007) == 0) { + dma_block_size = DMA_BLOCK_SIZE_8B; + divisor = 8; + } else if ((delta & 0x00000003) == 0) { + dma_block_size = DMA_BLOCK_SIZE_4B; + divisor = 4; + } else if ((delta & 0x00000001) == 0) { + dma_block_size = DMA_BLOCK_SIZE_2B; + divisor = 2; + } else { + dma_block_size = DMA_BLOCK_SIZE_1B; + divisor = 1; + } + + dma_src = ((_src + divisor - 1) / divisor) * divisor; + dma_dst = ((_dst + divisor - 1) / divisor) * divisor; + gap_size_pre = dma_dst - _dst; + dma_block_count = (sz - gap_size_pre) / divisor; + gap_size_end = (sz - gap_size_pre) - (dma_block_count * divisor); + + id = sh7305_dma_transfert_async( + dma_src, DMA_ADDR_INC, + dma_dst, DMA_ADDR_INC, + dma_block_size, + dma_block_count + ); + if (id < 0) + return id; + + for (int i = 0; i < gap_size_pre; ++i) { + ((uint8_t*)_dst)[0] = ((uint8_t*)_src)[0]; + _dst += 1; + _src += 1; + } + _dst += dma_block_count * divisor; + _src += dma_block_count * divisor; + for (int i = 0; i < gap_size_end; ++i) { + ((uint8_t*)_dst)[0] = ((uint8_t*)_src)[0]; + _dst += 1; + _src += 1; + } + + return id; } diff --git a/src/driver/mpu/sh/sh7305/dma/memset.c b/src/driver/mpu/sh/sh7305/dma/memset.c index 7399646..3345523 100644 --- a/src/driver/mpu/sh/sh7305/dma/memset.c +++ b/src/driver/mpu/sh/sh7305/dma/memset.c @@ -1,9 +1,66 @@ +#include #include +#include + +/* Allocate a 32-byte buffer in ILRAM */ +VALIGNED(32) VSECTION(".xram") static uint32_t Xbuf[8]; + /* dma_memset() : memset using the DMA */ dma_id_t sh7305_dma_memset(void *dst, int c, size_t sz) { - (void)dst; - (void)c; - (void)sz; + uintptr_t dma_src; + uintptr_t dma_dst; + ptrdiff_t gap_size_pre; + ptrdiff_t gap_size_end; + uint32_t mask; + int dma_block_count; + dma_id_t id; + + if (sz < 64) { + memset(dst, c, sz); + return 0; + } + + mask = ((c & 0xff) << 24) + | ((c & 0xff) << 16) + | ((c & 0xff) << 8) + | ((c & 0xff) << 0); + + /* Prepare the XRAM buffer. We need to use XRAM because the DMA will + have to read the operand once per block, as opposed to an assembler + routine that would hold it in a register. If we place it in RAM, the + DMA will perform twice as many RAM accesses as the handwritten + assembler, which would be very slow. By using XRAM we use two + different memory regions, making the DMA faster than the CPU. */ + for(int i = 0; i < 8; i++) Xbuf[i] = mask; + + //FIXME: SPU + + dma_src = (uintptr_t)Xbuf; + dma_dst = (uintptr_t)dst & 0xffffffe0; + gap_size_pre = dma_dst - (uintptr_t)dst; + dma_block_count = (sz - gap_size_pre) >> 5; + gap_size_end = (sz - gap_size_pre) - (dma_block_count << 5); + + id = sh7305_dma_transfert_async( + dma_src, DMA_ADDR_FIXED, + dma_dst, DMA_ADDR_INC, + DMA_BLOCK_SIZE_32B, + dma_block_count + ); + if (id < 0) + return id; + + for (int i = 0; i < gap_size_pre; ++i) { + ((uint8_t*)dst)[0] = c; + dst = &((uint8_t*)dst)[1]; + } + dst += dma_block_count << 5; + for (int i = 0; i < gap_size_end; ++i) { + ((uint8_t*)dst)[0] = c; + dst = &((uint8_t*)dst)[1]; + } + + return id; } diff --git a/src/driver/mpu/sh/sh7305/dma/wait.c b/src/driver/mpu/sh/sh7305/dma/wait.c new file mode 100644 index 0000000..a0d5fca --- /dev/null +++ b/src/driver/mpu/sh/sh7305/dma/wait.c @@ -0,0 +1,40 @@ +#include + +//--- +// Internal functions +//--- + +static int sh7305_dma_wait_core(sh7305_dma_id_t id, int atomic) +{ + sh7305_dma_channel_t *ch; + + ch = sh7305_dma_channel_find(id); + if (ch == NULL) + return -1; + + if (ch->id.KEYFRAME == id.KEYFRAME) + sh7305_dma_channel_wait(ch, atomic); + return 0; +} + +//--- +// User-level API +//--- + +/* dma_wait() : wait the end of the DMA channel transfer */ +int sh7305_dma_wait(dma_id_t id) +{ + sh7305_dma_id_t _id; + + _id.lword = id; + return sh7305_dma_wait_core(_id, 0); +} + +/* dma_wait() : wait the end of the DMA channel transfer without interruption */ +int sh7305_dma_spinwait(dma_id_t id) +{ + sh7305_dma_id_t _id; + + _id.lword = id; + return sh7305_dma_wait_core(_id, 1); +} diff --git a/src/driver/mpu/sh/sh7305/intc/intc.c b/src/driver/mpu/sh/sh7305/intc/intc.c index 454c855..7275a83 100644 --- a/src/driver/mpu/sh/sh7305/intc/intc.c +++ b/src/driver/mpu/sh/sh7305/intc/intc.c @@ -128,15 +128,14 @@ void *sh7305_intc_install_inth_generic(int event_code, vhex_call_t callback) uint8_t *h = sh7305_intc_install_inth_gate( event_code, - sh7305_intc_inth_generic_gate, + &sh7305_intc_inth_generic_gate, 32 ); - if(h == NULL) - return false; - - memcpy(&h[8], &callback, 20); - memcpy(&h[28], &sh7305_inth_callback, 4); - + if(h != NULL) { + uintptr_t workaround = (uintptr_t)&sh7305_inth_callback; + memcpy(&h[8], &callback, 20); + memcpy(&h[28], &workaround, 4); + } return h; }