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
This commit is contained in:
parent
7e01fb8444
commit
b8abc2eae7
|
@ -11,6 +11,7 @@ modules = [
|
|||
'kmalloc',
|
||||
'timer',
|
||||
'rtc',
|
||||
'dma'
|
||||
]
|
||||
|
||||
[drivers]
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
|
||||
#include <vhex/defs/types.h>
|
||||
|
||||
typedef uint32_t dma_id_t;
|
||||
typedef int32_t dma_id_t;
|
||||
|
||||
#endif /* __VHEX_DMA_TYPES__ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <vhex/defs/attributes.h>
|
||||
#include <vhex/defs/types.h>
|
||||
|
||||
#include <vhex/dma/types.h>
|
||||
|
||||
//---
|
||||
// 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__ */
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <vhex/defs/types.h>
|
||||
#include <vhex/defs/attributes.h>
|
||||
|
||||
#include <vhex/keyboard/keycode.h>
|
||||
|
||||
/* 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__ */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1,31 +1,253 @@
|
|||
#include <vhex/driver/mpu/sh/sh7305/dma.h>
|
||||
#include <vhex/driver/mpu/sh/sh7305/intc.h>
|
||||
#include <vhex/driver/cpu.h>
|
||||
//#include <vhex/driver/mpu/sh/sh7305/power.h>
|
||||
#include <vhex/driver.h>
|
||||
#include <vhex/defs/call.h>
|
||||
#include <vhex/dma/interface.h>
|
||||
|
||||
#include <vhex/display.h>
|
||||
|
||||
|
||||
/* 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
|
||||
|
|
|
@ -1,9 +1,79 @@
|
|||
#include <vhex/driver/mpu/sh/sh7305/dma.h>
|
||||
#include <vhex/dma.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,66 @@
|
|||
#include <vhex/driver/mpu/sh/sh7305/dma.h>
|
||||
#include <vhex/dma.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
#include <vhex/driver/mpu/sh/sh7305/dma.h>
|
||||
|
||||
//---
|
||||
// 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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue