Files
pico-loader/arm9/source/patches/platform/acekard-common/IoRPGLoaderPlatform.cpp
2025-11-23 17:14:17 +01:00

156 lines
4.1 KiB
C++

#include "common.h"
#include <libtwl/card/card.h>
#include "ioRPGDefinitions.h"
#include "../SdioDefinitions.h"
#include "waitByLoop.h"
#include "IoRPGLoaderPlatform.h"
bool IoRPGLoaderPlatform::InitializeSdCard()
{
bool isSdhc = false;
bool isSdVersion2 = false;
u32 responseR1 = 0;
// CMD0
SdSendSdioCommand(IoRpgCmdSdio(SD_CMD0_GO_IDLE_STATE, IORPG_SDIO_NORESPONSE, 0), nullptr, 0);
// CMD8
responseR1 = SdSendR1Command(SD_CMD8_SEND_IF_COND, SD_IF_COND_PATTERN);
isSdVersion2 = responseR1 == SD_IF_COND_PATTERN;
for (u32 i = 0; i < 10000; i++)
{
// CMD55
SdSendR1Command(SD_CMD55_APP_CMD, 0);
// ACMD41
u32 argument = 0x00FF8000;
if (isSdVersion2)
{
argument |= BIT(30);
}
responseR1 = SdSendR1Command(SD_ACMD41_SD_SEND_OP_COND, argument);
if (responseR1 & BIT(31))
{
isSdhc = responseR1 & BIT(30);
break;
}
waitByLoop(1666);
}
// CMD2
SdSendR2Command(SD_CMD2_ALL_SEND_CID, 0);
// CMD3
responseR1 = SdSendR1Command(SD_CMD3_SEND_RELATIVE_ADDR, 0);
u32 sdioRca = responseR1 >> 16;
// CMD9
SdSendR2Command(SD_CMD9_SEND_CSD, (sdioRca << 16));
// CMD7
SdSendR1Command(SD_CMD7_SELECT_CARD, (sdioRca << 16));
// ACMD6
SdSendR1Command(SD_CMD55_APP_CMD, (sdioRca << 16));
SdSendR1Command(SD_ACMD6_SET_BUS_WIDTH, 2);
// CMD13
responseR1 = SdSendR1Command(SD_CMD13_SEND_STATUS, (sdioRca << 16));
bool isSuccess = (responseR1 >> 9) == 4;
if (isSuccess && isSdhc)
{
card_romSetCmd(IORPG_CMD_SET_SD_MODE_SDHC);
card_romStartXfer(IORPG_CTRL_POLL, false);
card_romWaitBusy();
PatchSdscShift();
}
return isSuccess;
}
void IoRPGLoaderPlatform::SdSendSdioCommand(u64 command, u8* buffer, u32 length) const
{
u32 flags;
if (length == 0)
{
flags = IORPG_CTRL_POLL | MCCNT1_LATENCY1(80);
}
else
{
// Actually the original driver used 4KB reads.
// But with SDIO, it only really reads up to 136 bytes,
// so 4KB is unnecessary.
flags = IORPG_CTRL_READ_512B | MCCNT1_LATENCY1(40);
}
// param SDIO, get response
card_romSetCmd(command);
card_romStartXfer(flags, false);
// don't poll if we aren't reading
if (length == 0)
{
card_romWaitBusy();
return;
}
u32 i = 0;
u32 data[2];
u8* target = buffer + (length >> 3);
do
{
// Read data if available
if (card_romIsDataReady())
{
data[i & 1] = card_romGetData();
if (i & 1)
{
target--;
// Since every byte is a bit, read two words,
// then pack it into a single byte to represent the
// true value
// BIT(7) is the needed bit from the return values
for (int j = 0; j < 8; ++j)
{
u8 bit = (((u8*)data)[7 - j] & BIT(7)) >> 7;
*target |= bit << j;
}
}
i++;
}
// Exit loop when we reach the number of words needed
if (i >= (length >> 2))
{
break;
}
} while (card_romIsBusy());
// Here we cut the transfer short. We don't actually read all 512 bytes as seen
// in MCCNT1.
// Cutting the transfer length in the middle of transfer causes undefined
// behaviour, so wait until then
card_romWaitDataReady();
// add a delay just in case
waitByLoop(33);
// Cut transmission
REG_MCCNT1 = 0;
card_romStartXfer(IORPG_CTRL_POLL, false);
card_romWaitBusy();
}
u32 IoRPGLoaderPlatform::SdSendR1Command(u8 cmd, u32 argument) const
{
u64 buffer = 0;
SdSendSdioCommand(IoRpgCmdSdio(cmd, IORPG_SDIO_READ_RESPONSE, argument), (u8*)&buffer, SD_R1_RESPONSE_LENGTH_BITS);
return (u32)(buffer >> 9);
}
void IoRPGLoaderPlatform::SdSendR2Command(u8 cmd, u32 argument) const
{
ALIGN(4) u8 ret[136 >> 3] = {};
SdSendSdioCommand(IoRpgCmdSdio(cmd, IORPG_SDIO_READ_RESPONSE, argument), ret, SD_R2_RESPONSE_LENGTH_BITS);
}