Skip to content

Commit

Permalink
- Use ISR-based read method for SD Gecko.
Browse files Browse the repository at this point in the history
  • Loading branch information
Extrems committed Aug 3, 2020
1 parent 76dd363 commit bfe9720
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 35 deletions.
9 changes: 5 additions & 4 deletions cube/patches/Makefile
Expand Up @@ -30,17 +30,18 @@ clean:
build-sd:
@echo Building SD Patch ...
@$(CC) -Os $(OPTS) -c base/base.S
@$(CC) -Os $(OPTS) -c base/emulator.c -DDTK
@$(CC) -Os $(OPTS) -c base/emulator.c -DASYNC_READ -DDTK -DISR
@$(CC) -Os $(OPTS) -c base/fifo.c
@$(CC) -Os $(OPTS) -c base/igr.c
@$(CC) -Os $(OPTS) -c base/mix.c
@$(CC) -Os $(OPTS) -c base/blockdevice.c
@$(CC) -Os $(OPTS) -c sdgecko/sd.c -DSINGLE_SECTOR=2
@$(CC) -Os $(OPTS) -c base/blockdevice.c -DASYNC_READ
@$(CC) -Os $(OPTS) -c sdgecko/sd.c -DISR_READ=1 -DSINGLE_SECTOR=1
@$(CC) -Os $(OPTS) -c sdgecko/sd_isr.S
@$(CC) -Os $(OPTS) -c base/adp.c
@$(CC) -Os $(OPTS) -c base/frag.c
@$(CC) -Os $(OPTS) -c base/usbgecko.c
@$(CC) -Os $(OPTS) -c base/dolphin/os.c
@$(CC) -Os $(OPTS) -o sd.elf -T base/base.ld -T base/common.ld -T base/dolphin/os.ld base.o emulator.o fifo.o igr.o mix.o blockdevice.o sd.o adp.o frag.o usbgecko.o os.o
@$(CC) -Os $(OPTS) -o sd.elf -T base/base.ld -T base/common.ld -T base/dolphin/os.ld base.o emulator.o fifo.o igr.o mix.o blockdevice.o sd.o sd_isr.o adp.o frag.o usbgecko.o os.o
@rm -rf *.o
@$(OBJDUMP) -D sd.elf > $(DISASM)/sd_disasm.txt
@$(OBJCOPY) -O binary sd.elf sd.bin
Expand Down
18 changes: 14 additions & 4 deletions cube/patches/base/emulator.c
Expand Up @@ -132,7 +132,9 @@ bool dtk_fill_buffer(void)
dtk_fill_buffer();
}

#ifndef ISR
DCInvalidateRange(__builtin_assume_aligned(dtk.buffer, 32), sizeof(*dtk.buffer));
#endif
read_disc_frag(__builtin_assume_aligned(dtk.buffer, 32), sizeof(*dtk.buffer), dtk.current.position, read_callback);
#else
OSCancelAlarm(&read_alarm);
Expand Down Expand Up @@ -693,6 +695,14 @@ void exception_handler(OSException exception, OSContext *context, ...);
extern void load_context(void) __attribute((noreturn));
extern uint32_t load_context_end[];

void external_interrupt_vector(void);

static void write_branch(void *a, void *b)
{
*(uint32_t *)a = (uint32_t)(b - (OS_BASE_CACHED - 0x48000002));
asm volatile("dcbst 0,%0; sync; icbi 0,%0" :: "r" (a));
}

void service_exception(OSException exception, OSContext *context, uint32_t dsisr, uint32_t dar)
{
OSExceptionHandler handler;
Expand All @@ -707,10 +717,7 @@ void service_exception(OSException exception, OSContext *context, uint32_t dsisr
break;
}
if (handler) {
ptrdiff_t offset = (ptrdiff_t)handler - (ptrdiff_t)load_context_end;

*load_context_end = 0x48000000 | (offset & 0x3FFFFFC);
asm volatile("dcbst 0,%0; sync; icbi 0,%0" :: "r" (load_context_end));
write_branch(load_context_end, handler);
load_context();
return;
}
Expand Down Expand Up @@ -773,6 +780,9 @@ void init(void)
OSCreateAlarm(&command_alarm);

OSSetExceptionHandler(OS_EXCEPTION_DSI, exception_handler);
#ifdef ISR
write_branch((void *)0x80000500, external_interrupt_vector);
#endif
}

bool exi_probe(int32_t chan)
Expand Down
1 change: 0 additions & 1 deletion cube/patches/base/frag.c
Expand Up @@ -129,5 +129,4 @@ void device_frag_read(void *dst, u32 len, u32 offset)
dst+=amountRead;
offset+=amountRead;
}
end_read();
}
1 change: 1 addition & 0 deletions cube/patches/base/igr.c
Expand Up @@ -51,6 +51,7 @@ static void load_dol(uint32_t offset, uint32_t size)
dcache_flush_icache_inv(image.data[i], image.dataLen[i]);
}

end_read();
image.entry();
}

Expand Down
137 changes: 120 additions & 17 deletions cube/patches/sdgecko/sd.c
Expand Up @@ -4,9 +4,11 @@
#**************************************************************************/

#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "../base/common.h"
#include "../base/dolphin/exi.h"
#include "../base/dolphin/os.h"

//CMD12 - Stop multiple block read command
#define CMD12 0x4C
Expand All @@ -25,11 +27,28 @@
#define exi_channel (*(u8*)VAR_EXI_SLOT)
#define exi_regs (*(vu32**)VAR_EXI_REGS)

static OSInterruptHandler TCIntrruptHandler = NULL;

static struct {
uint32_t next_sector;
uint32_t last_sector;
#if ISR_READ
int items;
struct {
void *address;
uint32_t length;
uint32_t offset;
uint32_t sector;
read_frag_cb callback;
} queue[2];
#endif
} mmc = {0};

extern intptr_t isr_registers;
extern int isr_transferred;

void tc_interrupt_handler(OSInterrupt interrupt, OSContext *context);

// EXI Functions
static void exi_select()
{
Expand Down Expand Up @@ -94,22 +113,34 @@ static void exi_read_to_buffer(void *dest, u32 len) {
}
}

static void rcvr_datablock(void *dest, u32 start_byte, u32 bytes_to_read) {
static void rcvr_datablock(void *dest, u32 start_byte, u32 bytes_to_read, int sync) {
exi_select();

while(rcvr_spi() != 0xFE);

// Skip the start if it's a misaligned read
exi_read_to_buffer(0, start_byte);

// Read however much we need to in this block
exi_read_to_buffer(dest, bytes_to_read);
if(sync) {
// Skip the start if it's a misaligned read
exi_read_to_buffer(0, start_byte);

// Read out the rest from the SD as we've requested it anyway and can't have it hanging off the bus (2 for CRC discard)
u32 remainder = 2 + (SECTOR_SIZE - (start_byte+bytes_to_read));
exi_read_to_buffer(0, remainder);
// Read however much we need to in this block
exi_read_to_buffer(dest, bytes_to_read);

exi_deselect();
// Read out the rest from the SD as we've requested it anyway and can't have it hanging off the bus (2 for CRC discard)
u32 remainder = 2 + (SECTOR_SIZE - (start_byte+bytes_to_read));
exi_read_to_buffer(0, remainder);

exi_deselect();
}
else {
#if ISR_READ
isr_registers = OSUncachedToPhysical(exi_regs);
isr_transferred = -4;

OSInterrupt interrupt = OS_INTERRUPT_EXI_0_TC + (3 * exi_channel);
TCIntrruptHandler = OSSetInterruptHandler(interrupt, tc_interrupt_handler);
OSUnmaskInterrupts(OS_INTERRUPTMASK(interrupt));
#endif
}
}

#ifndef SINGLE_SECTOR
Expand All @@ -128,7 +159,7 @@ u32 do_read(void *dst, u32 len, u32 offset, u32 sectorLba) {
if(startByte) {
// amount to read in first block may vary if our read is small enough to fit in it
u32 amountInBlock = (len + startByte > SECTOR_SIZE) ? SECTOR_SIZE-startByte : len;
rcvr_datablock(dst,startByte, amountInBlock);
rcvr_datablock(dst,startByte, amountInBlock, 1);
numBytes-=amountInBlock;
dst+=amountInBlock;
}
Expand All @@ -137,21 +168,93 @@ u32 do_read(void *dst, u32 len, u32 offset, u32 sectorLba) {
u32 numFullBlocks = numBytes>>9;
numBytes -= numFullBlocks << 9;
while(numFullBlocks) {
rcvr_datablock(dst, 0,SECTOR_SIZE);
rcvr_datablock(dst, 0,SECTOR_SIZE, 1);
dst+=SECTOR_SIZE;
numFullBlocks--;
}

// Read any trailing half block
if(numBytes) {
rcvr_datablock(dst,0, numBytes);
rcvr_datablock(dst,0, numBytes, 1);
dst+=numBytes;
}
// End the read by sending CMD12
send_cmd(CMD12, 0);
return len;
}
#else
#if ISR_READ
static void mmc_read_queued(void)
{
if (!EXILock(exi_channel, EXI_DEVICE_0, (EXICallback)mmc_read_queued))
return;

void *address = mmc.queue[0].address;
uint32_t length = mmc.queue[0].length;
uint32_t offset = mmc.queue[0].offset;
uint32_t sector = mmc.queue[0].sector;

if (sector != mmc.next_sector) {
end_read();
send_cmd(CMD18, sector);
}

rcvr_datablock(sectorBuf, 0, SECTOR_SIZE, 0);
}

void do_read_disc(void *address, uint32_t length, uint32_t offset, uint32_t sector, read_frag_cb callback)
{
int i;

for (i = 0; i < mmc.items; i++)
if (mmc.queue[i].callback == callback)
return;

sector = offset / SECTOR_SIZE + sector;
offset = offset % SECTOR_SIZE;
length = MIN(length, SECTOR_SIZE - offset);

mmc.queue[i].address = address;
mmc.queue[i].length = length;
mmc.queue[i].offset = offset;
mmc.queue[i].sector = sector;
mmc.queue[i].callback = callback;
if (mmc.items++) return;

mmc_read_queued();
}

void tc_interrupt_handler(OSInterrupt interrupt, OSContext *context)
{
if (isr_transferred < SECTOR_SIZE)
return;

OSMaskInterrupts(OS_INTERRUPTMASK(interrupt));
OSSetInterruptHandler(interrupt, TCIntrruptHandler);
exi_imm_read(2);
exi_deselect();
EXIUnlock(exi_channel);

void *address = mmc.queue[0].address;
uint32_t length = mmc.queue[0].length;
uint32_t offset = mmc.queue[0].offset;
uint32_t sector = mmc.queue[0].sector;
read_frag_cb callback = mmc.queue[0].callback;
mmc.last_sector = sector;
mmc.next_sector = sector + (1 << *VAR_SD_SHIFT);

if (address != VAR_SECTOR_BUF + offset)
memcpy(address, sectorBuf + offset, length);

if (--mmc.items) {
memcpy(mmc.queue, mmc.queue + 1, mmc.items * sizeof(*mmc.queue));
mmc_read_queued();
}

callback(address, length);
}
#endif

u32 do_read(void *dst, u32 len, u32 offset, u32 sectorLba) {
// Try locking EXI bus
if(EXILock && !EXILock(exi_channel, EXI_DEVICE_0, 0)) {
Expand All @@ -169,7 +272,7 @@ u32 do_read(void *dst, u32 len, u32 offset, u32 sectorLba) {
// Send single block read command and the LBA we want to read at
send_cmd(CMD17, lba);
// Read block
rcvr_datablock(dst, startByte, numBytes);
rcvr_datablock(dst, startByte, numBytes, 1);
#else
// If we saved this sector
if(lba == mmc.last_sector) {
Expand All @@ -184,14 +287,14 @@ u32 do_read(void *dst, u32 len, u32 offset, u32 sectorLba) {
}
if(numBytes < SECTOR_SIZE) {
// Read half block
rcvr_datablock(sectorBuf, 0, SECTOR_SIZE);
rcvr_datablock(sectorBuf, 0, SECTOR_SIZE, 1);
memcpy(dst, sectorBuf + startByte, numBytes);
// Save current LBA
mmc.last_sector = lba;
}
else {
// Read full block
rcvr_datablock(dst, 0, SECTOR_SIZE);
rcvr_datablock(dst, 0, SECTOR_SIZE, 1);
// If we're reusing the sector buffer
if(dst == VAR_SECTOR_BUF) {
// Save current LBA
Expand All @@ -209,7 +312,7 @@ u32 do_read(void *dst, u32 len, u32 offset, u32 sectorLba) {
#endif

void end_read() {
#if SINGLE_SECTOR == 2
#if SINGLE_SECTOR == 2 || ISR_READ
if(mmc.next_sector) {
mmc.next_sector = 0;
// End the read by sending CMD12
Expand Down
78 changes: 78 additions & 0 deletions cube/patches/sdgecko/sd_isr.S
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2020, Extrems <extrems@extremscorner.org>
*
* This file is part of Swiss.
*
* Swiss is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Swiss is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* with Swiss. If not, see <https://www.gnu.org/licenses/>.
*/

#define _LANGUAGE_ASSEMBLY
#include "../../reservedarea.h"

.section .text.external_interrupt_vector,"ax",@progbits
.balign 32
.globl isr_registers, isr_transferred
isr_registers:
.long 0x0C006800
isr_transferred:
.long 512

.globl external_interrupt_vector
external_interrupt_vector:
mtsprg 0, r4
mtsprg 1, r5
mtsprg 2, r6
mfcr r6
mtsprg 3, r6
lwz r4, isr_registers - 0x80000000 (r0)
eciwx r5, r0, r4
rlwinm r6, r5, 1, 28, 28
and. r6, r5, r6
beq 3f
andi. r5, r5, (0x3FFF & ~0x80A) | (1 << 3)
lwz r6, isr_transferred - 0x80000000 (r0)
cmpwi r6, 512 - 4
bgt 3f
addi r6, r6, 4
stw r6, isr_transferred - 0x80000000 (r0)
beq 1f
cmpwi r6, 0
ecowx r5, r0, r4
ble 2f
1: li r5, 4*4
eciwx r5, r5, r4
stw r5, VAR_SECTOR_BUF - 4 (r6)
beq 3f
2: li r5, 4*4
li r6, -1
ecowx r6, r5, r4
li r5, 3*4
li r6, ((4 - 1) << 4) | 0b01
ecowx r6, r5, r4
lis r4, 0x0C00
li r5, 0x3000
eciwx r5, r5, r4
rlwinm. r6, r5, 0, 16, 14
bne 3f
mfsprg r6, 3
mtcr r6
mfsprg r6, 2
mfsprg r5, 1
mfsprg r4, 0
rfi
3: mfsprg r6, 3
mtcr r6
mfsprg r6, 2
mfsprg r5, 1
ba 0x00000504

0 comments on commit bfe9720

Please sign in to comment.