From 2ceafc545b254180563a56fca9013a9aa310fd8c Mon Sep 17 00:00:00 2001 From: Balint Kovacs Date: Mon, 8 Jul 2019 15:27:44 +0200 Subject: [PATCH] First viable(-ish) prototype For some reason (messed up memory access?) the first two time I read the status register, I get garbage. Also: * Reinserting card breaks SPI (everything reads 0xff * No support for CTR carts for now --- arm9/source/gamecart/spi.c | 117 +++++++++------------------ arm9/source/gamecart/spicard.c | 139 +++++++++++++++++++++++++++++++++ arm9/source/gamecart/spicard.h | 96 +++++++++++++++++++++++ 3 files changed, 273 insertions(+), 79 deletions(-) create mode 100644 arm9/source/gamecart/spicard.c create mode 100644 arm9/source/gamecart/spicard.h diff --git a/arm9/source/gamecart/spi.c b/arm9/source/gamecart/spi.c index 550cc91..21d3c05 100644 --- a/arm9/source/gamecart/spi.c +++ b/arm9/source/gamecart/spi.c @@ -17,103 +17,42 @@ */ #include "spi.h" +#include "spicard.h" +#include "ui.h" // Deliberately written in C! (except for a few lines) // u8* fill_buf = NULL; -#define CFG_CARDCONF (*(vu16 *)0x1000000C) +int SPIWriteRead(CardType type, void* cmd, u32 cmdSize, void* answer, u32 answerSize, void* data, u32 dataSize) { + const u32 headerFooterVal = 0; + bool b = type == FLASH_512KB_INFRARED || type == FLASH_256KB_INFRARED; -#define REG_SPICARDCNT (*(vu32 *)0x1000D800) -#define REG_SPICARDASSERT (*(vu32 *)0x1000D804) -#define REG_SPICARDSIZE (*(vu32 *)0x1000D808) -#define REG_SPICARDFIFO (*(vu32 *)0x1000D80C) -#define REG_SPICARDFIFOSTAT (*(vu32 *)0x1000D810) -#define REG_UNK_AT_0x18 (*(vu32 *)0x1000D818) + SPICARD_init(); -#define SPICARD_START_IS_BUSY 0x8000 - -//Thanks @Steveice10 for giving me his P9 symbols (IDB file). -//This code is compatible with SPI.c/SPI.h from TWLSaveTool/// Card SPI baud rate. - -//taken over from ctrulib fs.h -typedef enum { - BAUDRATE_512KHZ = 0, ///< 512KHz. - BAUDRATE_1MHZ = 1, ///< 1MHz. - BAUDRATE_2MHZ = 2, ///< 2MHz. - BAUDRATE_4MHZ = 3, ///< 4MHz. - BAUDRATE_8MHZ = 4, ///< 8MHz. - BAUDRATE_16MHZ = 5, ///< 16MHz. -} FS_CardSpiBaudRate; - - -void _SPITransferData(void *data, u32 len, FS_CardSpiBaudRate baudRate, bool write) -{ - REG_SPICARDSIZE = len; - REG_SPICARDCNT = (((write) ? 1 : 0) << 13) | (0 << 12) | (u32)baudRate; - REG_UNK_AT_0x18 = 0; - REG_SPICARDCNT |= SPICARD_START_IS_BUSY; //start - - u32 wordCount = (len + 3) >> 2; - u32 len_was = len; - for(u32 i = 0; i < wordCount; i++) - { - u32 nbBytes = (len <= 4) ? len : 4; - - if(write) - { - u32 word = 0; - memcpy(&word, (u32 *)data + i, nbBytes); - while(REG_SPICARDFIFOSTAT); - REG_SPICARDFIFO = word; - } - - else - { - while(!REG_SPICARDFIFOSTAT); - u32 word = REG_SPICARDFIFO; - memcpy((u32 *)data + i, &word, nbBytes); - } - - len -= nbBytes; + if (b) { + SPICARD_writeRead(NSPI_CLK_1MHz, &headerFooterVal, NULL, 1, 0, false); } - - while(REG_SPICARDCNT & SPICARD_START_IS_BUSY) ShowString("Busy, %s %lu", (write) ? "write" : "read", len_was); -} - -int SPIWriteRead(CardType type, void* cmd, u32 cmdSize, void* answer, u32 answerSize, void* data, u32 dataSize) -{ - bool infra = type == FLASH_512KB_INFRARED || type == FLASH_256KB_INFRARED; - - CFG_CARDCONF |= 0x100; //wake card - - u32 zero = 0; - if(infra) _SPITransferData(&zero, 1, BAUDRATE_1MHZ, true); //header - - if(cmd != NULL) _SPITransferData(cmd, cmdSize, BAUDRATE_4MHZ, true); - if(answer != NULL) _SPITransferData(answer, answerSize, BAUDRATE_4MHZ, false); - if(data != NULL) _SPITransferData(data, dataSize, BAUDRATE_4MHZ, true); - if (dataSize) ShowPrompt(false, "Completed: %lu/%lu/%lu", cmdSize, answerSize, dataSize); - // else ShowString("Completed: %lu/%lu/%lu", cmdSize, answerSize, dataSize); - REG_SPICARDASSERT = 0; + SPICARD_writeRead(NSPI_CLK_4MHz, cmd, answer, cmdSize, answerSize, false); + SPICARD_writeRead(NSPI_CLK_4MHz, data, NULL, dataSize, 0, true); return 0; } int SPIWaitWriteEnd(CardType type) { - u32 cmd = SPI_CMD_RDSR, statusReg = 0; + u8 cmd = SPI_CMD_RDSR, statusReg = 0; int res = 0; - ShowPrompt(false, "WaitWriteEnd start"); + do{ res = SPIWriteRead(type, &cmd, 1, &statusReg, 1, 0, 0); if(res) return res; } while(statusReg & SPI_FLG_WIP); - ShowPrompt(false, "WaitWriteEnd complete"); + return 0; } int SPIEnableWriting(CardType type) { - u32 cmd = SPI_CMD_WREN, statusReg = 0; + u8 cmd = SPI_CMD_WREN, statusReg = 0; int res = SPIWriteRead(type, &cmd, 1, NULL, 0, 0, 0); if(res || type == EEPROM_512B) return res; // Weird, but works (otherwise we're getting an infinite loop for that chip type). @@ -128,9 +67,9 @@ int SPIEnableWriting(CardType type) { } int SPIReadJEDECIDAndStatusReg(CardType type, u32* id, u8* statusReg) { - u32 cmd = SPI_FLASH_CMD_RDID; - u32 reg = 0; - u8 idbuf[4] = { 0 }; + u8 cmd = SPI_FLASH_CMD_RDID; + u8 reg = 0; + u8 idbuf[3] = { 0 }; u32 id_ = 0; int res = SPIWaitWriteEnd(type); if(res) return res; @@ -145,6 +84,8 @@ int SPIReadJEDECIDAndStatusReg(CardType type, u32* id, u8* statusReg) { if(id) *id = id_; if(statusReg) *statusReg = reg; + ShowPrompt(false, "JEDEC = %lx, StatusReg = %hhx", *id, reg); + return 0; } @@ -409,6 +350,24 @@ int SPIGetCardType(CardType* type, int infrared) { while(tries < maxTries){ res = SPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy if(res) return res; + res = SPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy + if(res) return res; + res = SPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy + if(res) return res; + res = SPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy + if(res) return res; + res = SPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy + if(res) return res; + res = SPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy + if(res) return res; + res = SPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy + if(res) return res; + res = SPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy + if(res) return res; + res = SPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy + if(res) return res; + res = SPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy + if(res) return res; if ((sr & 0xfd) == 0x00 && (jedec != 0x00ffffff)) { break; } if ((sr & 0xfd) == 0xF0 && (jedec == 0x00ffffff)) { t = EEPROM_512B; break; } @@ -417,7 +376,7 @@ int SPIGetCardType(CardType* type, int infrared) { ++tries; t = FLASH_INFRARED_DUMMY; } - ShowPrompt(false, "JEDECID done"); + if(t == EEPROM_512B) { *type = t; return 0; } else if(t == EEPROM_STD_DUMMY) { bool mirrored = false; diff --git a/arm9/source/gamecart/spicard.c b/arm9/source/gamecart/spicard.c new file mode 100644 index 0000000..448c4b3 --- /dev/null +++ b/arm9/source/gamecart/spicard.c @@ -0,0 +1,139 @@ +/* + * This file is part of fastboot 3DS + * Copyright (C) 2019 derrek, profi200 + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 + * along with this program. If not, see . + */ + +#include "types.h" +#include "spicard.h" +#include "delay.h" + +#define SPICARD_REGS_BASE 0x1000D800 +#define REG_NSPI_CNT *((vu32*)(SPICARD_REGS_BASE + 0x00)) +#define REG_NSPI_DONE *((vu32*)(SPICARD_REGS_BASE + 0x04)) +#define REG_NSPI_BLKLEN *((vu32*)(SPICARD_REGS_BASE + 0x08)) +#define REG_NSPI_FIFO *((vu32*)(SPICARD_REGS_BASE + 0x0C)) +#define REG_NSPI_STATUS *((vu32*)(SPICARD_REGS_BASE + 0x10)) +#define REG_NSPI_AUTOPOLL *((vu32*)(SPICARD_REGS_BASE + 0x14)) +#define REG_NSPI_INT_MASK *((vu32*)(SPICARD_REGS_BASE + 0x18)) +#define REG_NSPI_INT_STAT *((vu32*)(SPICARD_REGS_BASE + 0x1C)) + + + +static inline void nspiWaitBusy(void) +{ + while(REG_NSPI_CNT & NSPI_CNT_ENABLE); +} + +static inline void nspiWaitFifoBusy(void) +{ + while(REG_NSPI_STATUS & NSPI_STATUS_BUSY); +} + +void SPICARD_init(void) +{ + static bool inited = false; + if(inited) return; + inited = true; + + // TODO +#define REG_CFG9_CARDCTL *((vu16*)0x1000000C) +#define REG_CFG9_CARDSTATUS *((vu8* )0x10000010) +#define REG_CFG9_CARDCYCLES0 *((vu16*)0x10000012) +#define REG_CFG9_CARDCYCLES1 *((vu16*)0x10000014) + +#define REG_NTRCARDMCNT *((vu16*)0x10164000) +#define REG_NTRCARDROMCNT *((vu32*)0x10164004) + + REG_CFG9_CARDCYCLES0 = 0x1988; + REG_CFG9_CARDCYCLES1 = 0x264C; + // boot9 waits here. Unnecessary? + + REG_CFG9_CARDSTATUS = 3u<<2; // Request power off + while(REG_CFG9_CARDSTATUS != 0); // Aotomatically changes to 0 (off) + ioDelay(1000); + + REG_CFG9_CARDSTATUS = 1u<<2; // Prepare power on + ioDelay(10000); + + REG_CFG9_CARDSTATUS = 2u<<2; // Power on + ioDelay(27000); + + // Switch to NTRCARD controller. + REG_CFG9_CARDCTL = 0; + REG_NTRCARDMCNT = 0xC000u; + REG_NTRCARDROMCNT = 0x20000000; + ioDelay(120000); + + REG_CFG9_CARDCTL |= 1u<<8; + + REG_NSPI_INT_MASK = NSPI_INT_TRANSF_END; // Disable interrupt 1 + REG_NSPI_INT_STAT = NSPI_INT_AP_TIMEOUT | NSPI_INT_AP_SUCCESS | NSPI_INT_TRANSF_END; // Aknowledge +} + +/* +bool _SPICARD_autoPollBit(u32 params) +{ + REG_NSPI_AUTOPOLL = NSPI_AUTOPOLL_START | params; + + u32 res; + do + { + __wfi(); + res = REG_NSPI_INT_STAT; + } while(!(res & (NSPI_INT_AP_TIMEOUT | NSPI_INT_AP_SUCCESS))); + REG_NSPI_INT_STAT = res; // Aknowledge + + return (res & NSPI_INT_AP_TIMEOUT) == 0; // Timeout error +} +*/ + +void SPICARD_writeRead(NspiClk clk, const u32 *in, u32 *out, u32 inSize, u32 outSize, bool done) +{ + const u32 cntParams = NSPI_CNT_ENABLE | NSPI_CNT_BUS_1BIT | clk; + + if(in) + { + REG_NSPI_BLKLEN = inSize; + REG_NSPI_CNT = cntParams | NSPI_CNT_DIRE_WRITE; + + u32 counter = 0; + do + { + if((counter & 31) == 0) nspiWaitFifoBusy(); + REG_NSPI_FIFO = *in++; + counter += 4; + } while(counter < inSize); + + nspiWaitBusy(); + } + if(out) + { + REG_NSPI_BLKLEN = outSize; + REG_NSPI_CNT = cntParams | NSPI_CNT_DIRE_READ; + + u32 counter = 0; + do + { + if((counter & 31) == 0) nspiWaitFifoBusy(); + *out++ = REG_NSPI_FIFO; + counter += 4; + } while(counter < outSize); + + nspiWaitBusy(); + } + + if(done) REG_NSPI_DONE = NSPI_DONE; +} diff --git a/arm9/source/gamecart/spicard.h b/arm9/source/gamecart/spicard.h new file mode 100644 index 0000000..4ecaaa9 --- /dev/null +++ b/arm9/source/gamecart/spicard.h @@ -0,0 +1,96 @@ +#pragma once + +/* + * This file is part of fastboot 3DS + * Copyright (C) 2019 derrek, profi200 + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 + * along with this program. If not, see . + */ + +#include "types.h" + + +// REG_NSPI_CNT +#define NSPI_CNT_BUS_1BIT (0u) +#define NSPI_CNT_BUS_4BIT (1u<<12) +#define NSPI_CNT_DIRE_READ (0u) +#define NSPI_CNT_DIRE_WRITE (1u<<13) +#define NSPI_CNT_ENABLE (1u<<15) + +// REG_NSPI_DONE +#define NSPI_DONE (0u) + +// REG_NSPI_STATUS +#define NSPI_STATUS_BUSY (1u) + +// REG_NSPI_AUTOPOLL +#define NSPI_AUTOPOLL_START (1u<<31) + +// REG_NSPI_INT_MASK Bit set = disabled. +// REG_NSPI_INT_STAT Status and aknowledge. +#define NSPI_INT_TRANSF_END (1u) // Fires on (each?) auto poll try aswell +#define NSPI_INT_AP_SUCCESS (1u<<1) // Auto poll +#define NSPI_INT_AP_TIMEOUT (1u<<2) // Auto poll + + +typedef enum +{ + NSPI_CLK_512KHz = 0u, + NSPI_CLK_1MHz = 1u, + NSPI_CLK_2MHz = 2u, + NSPI_CLK_4MHz = 3u, + NSPI_CLK_8MHz = 4u, + NSPI_CLK_16MHz = 5u +} NspiClk; + + + +/** + * @brief Initializes the SPI buses. Call this only once. + */ +void SPICARD_init(void); + +/** + * @brief Automatically polls a bit of the command response. Use with the macro below. + * + * @param[in] params The parameters. Use the macro below. + * + * @return Returns false on failure/timeout and true on success. + */ +bool _SPICARD_autoPollBit(u32 params); + +/** + * @brief Writes and/or reads data to/from a SPI device. + * + * @param[in] clk The clock frequency to use. + * @param[in] in Input data pointer for write. + * @param out Output data pointer for read. + * @param[in] inSize Input size. Must be <= 0x1FFFFF. + * @param[in] outSize Output size. Must be <= 0x1FFFFF. + * @param[in] done Set to true if this is the last transfer (chip select). + */ +void SPICARD_writeRead(NspiClk clk, const u32 *in, u32 *out, u32 inSize, u32 outSize, bool done); + + +/** + * @brief Automatically polls a bit of the command response. + * + * @param[in] cmd The command. + * @param[in] timeout The timeout. Must be 0-15. Tries = 31<