mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-06-02 09:16:49 +02:00
Add support for DATEL devices (GAMES n' MUSIC and Action Replay DS(i) Media Edition) (#64)
This commit is contained in:
154
arm9/source/patches/platform/datel/DatelLoaderPlatform.cpp
Normal file
154
arm9/source/patches/platform/datel/DatelLoaderPlatform.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/card/card.h>
|
||||
#include "../SdioDefinitions.h"
|
||||
#include "thumbInstructions.h"
|
||||
#include "DatelLoaderPlatform.h"
|
||||
|
||||
static constexpr int MAX_STARTUP_TRIES = 5000;
|
||||
static constexpr int SD_COMMAND_TIMEOUT = 0xFFF;
|
||||
static constexpr u32 DATEL_CTRL_BASE = (MCCNT1_RESET_OFF | MCCNT1_CMD_SCRAMBLE | MCCNT1_READ_DATA_DESCRAMBLE | MCCNT1_CLOCK_SCRAMBLER | MCCNT1_LATENCY2(0x3F));
|
||||
|
||||
static constexpr u8 DATEL_CMD_F2_SPI_ENABLE = 0xCC;
|
||||
static constexpr u8 DATEL_CMD_F2_SPI_DISABLE = 0xC8;
|
||||
|
||||
static inline u64 DATEL_CMD_F2(u32 param1, u8 param2)
|
||||
{
|
||||
return (0xF200000000000000ull | ((u64)param1 << 24) | ((u64)param2 << 16));
|
||||
}
|
||||
|
||||
static inline void enableSpi()
|
||||
{
|
||||
REG_MCCNT0 = MCCNT0_MODE_SPI | MCCNT0_SPI_HOLD_CS | MCCNT0_ENABLE;
|
||||
}
|
||||
|
||||
static u8 readWriteSpiByte(u8 data)
|
||||
{
|
||||
REG_MCD0 = data;
|
||||
while(REG_MCCNT0 & MCCNT0_SPI_BUSY);
|
||||
return REG_MCD0;
|
||||
}
|
||||
|
||||
static u8 readSpiByteTimeout()
|
||||
{
|
||||
auto timeout = SD_COMMAND_TIMEOUT;
|
||||
u8 res;
|
||||
do
|
||||
{
|
||||
res = readWriteSpiByte(0xFF);
|
||||
} while (res == 0xFF && --timeout > 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void sendNtrCommandF2(u32 param1, u8 param2)
|
||||
{
|
||||
card_romSetCmd(DATEL_CMD_F2(param1, param2));
|
||||
card_romStartXfer(DATEL_CTRL_BASE | MCCNT1_LEN_0, false);
|
||||
card_romWaitBusy();
|
||||
}
|
||||
|
||||
static void cycleSpi()
|
||||
{
|
||||
sendNtrCommandF2(0, DATEL_CMD_F2_SPI_DISABLE);
|
||||
enableSpi();
|
||||
readWriteSpiByte(0xFF);
|
||||
sendNtrCommandF2(0, DATEL_CMD_F2_SPI_ENABLE);
|
||||
enableSpi();
|
||||
}
|
||||
|
||||
// Sends SDIO command to DATEL device.
|
||||
static u8 spiSendSdioCommand(u8 cmdId, u32 arg, u8 * buffer, int messageLen)
|
||||
{
|
||||
cycleSpi();
|
||||
u8 cmd[6];
|
||||
|
||||
// Build a SPI SD command to be sent as-is.
|
||||
cmd[0] = 0x40 | (cmdId & 0x3f);
|
||||
cmd[1] = arg >> 24;
|
||||
cmd[2] = arg >> 16;
|
||||
cmd[3] = arg >> 8;
|
||||
cmd[4] = arg >> 0;
|
||||
// CRC in SPI mode is ignored for every command but CMD0 (hardcoded to 0x95)
|
||||
// and CMD8, hardcoded to 0x86 with the default 0x1AA argument.
|
||||
cmd[5] = (messageLen > 1) ? 0x86 : 0x95;
|
||||
|
||||
for (u8 byte : cmd)
|
||||
{
|
||||
readWriteSpiByte(byte);
|
||||
}
|
||||
|
||||
u8 timeout = readSpiByteTimeout();
|
||||
|
||||
for (int i = 0; i < (messageLen - 1); i++)
|
||||
{
|
||||
buffer[i] = readWriteSpiByte(0xFF);
|
||||
}
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static u8 spiSendSdioCommandR0(u8 cmd, u32 arg)
|
||||
{
|
||||
return spiSendSdioCommand(cmd, arg, nullptr, 1);
|
||||
}
|
||||
|
||||
static bool trySendAcmd41(u32 acmd41Arg)
|
||||
{
|
||||
for (int i = 0; i < MAX_STARTUP_TRIES; i++)
|
||||
{
|
||||
// Send ACMD41.
|
||||
spiSendSdioCommandR0(SD_CMD55_APP_CMD, 0);
|
||||
if (spiSendSdioCommandR0(SD_ACMD41_SD_SEND_OP_COND, acmd41Arg) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DatelLoaderPlatform::InitializeSdCard()
|
||||
{
|
||||
for (int i = 0; i < 0x100; i++)
|
||||
{
|
||||
sendNtrCommandF2(0x7FFFFFFF | ((i & 1) << 31), 0x00);
|
||||
}
|
||||
|
||||
// Send CMD0.
|
||||
if (spiSendSdioCommandR0(SD_CMD0_GO_IDLE_STATE, 0) != 0x01)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 cmd8Answer = 0;
|
||||
|
||||
u32 acmd41Arg = 0;
|
||||
bool isV2 = false;
|
||||
|
||||
if (spiSendSdioCommand(SD_CMD8_SEND_IF_COND, SD_IF_COND_PATTERN, (u8*)&cmd8Answer, 5) == 0x1
|
||||
&& cmd8Answer == 0xAA010000)
|
||||
{
|
||||
isV2 = true;
|
||||
acmd41Arg |= (1 << 30); // Set HCS bit,Supports SDHC
|
||||
}
|
||||
|
||||
if (!trySendAcmd41(acmd41Arg))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isSdhc = false;
|
||||
if (isV2)
|
||||
{
|
||||
u32 cmd58Answer = 0;
|
||||
spiSendSdioCommand(SD_SPI_CMD58_READ_OCR, 0, (u8*)&cmd58Answer, 5);
|
||||
isSdhc = (cmd58Answer & 0x40) != 0;
|
||||
}
|
||||
spiSendSdioCommandR0(SD_CMD16_SET_BLOCKLEN, 0x200);
|
||||
|
||||
const u16 nonSdhcOpcode = THUMB_LSLS_IMM(THUMB_R0, THUMB_R0, 9);
|
||||
const u16 sdhcOpcode = THUMB_MOVS_REG(THUMB_R0, THUMB_R0);
|
||||
const u16 opcode = isSdhc ? sdhcOpcode : nonSdhcOpcode;
|
||||
datel_writeSectorSdhcLabel = opcode;
|
||||
datel_readSectorSdhcLabel = opcode;
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user