GodMode9/arm9/source/system/spiflash.c
Wolfvak 7eb559cf21
NVRAM Write capability (#946)
* Writable NVRAM

Also refactors the spiflash module to detect chips at runtime
2026-02-21 20:51:42 -03:00

126 lines
2.8 KiB
C
Executable File

#include "common.h"
#include "arm.h"
#include "pxi.h"
#include "shmem.h"
#include "ui.h"
typedef struct {
u32 id;
u32 size;
const char *name;
} spiflash_chip_t;
/**
* List of known SPI flash chips available in the NDS/3DS family of systems.
* If it doesn't match any of these, it should be safe to assume we have a 128k flash chip.
* Obtained from gbatek (https://problemkaputt.de/gbatek-ds-firmware-serial-flash-memory.htm) as of 1/21/26.
*/
static const spiflash_chip_t spiflash_known_chips[] = {
{ 0x124020, 256u << 10, "ST M45PE20" },
{ 0x125020, 256u << 10, "ST M35PE20" },
{ 0x138020, 512u << 10, "ST M25PE40" },
{ 0x114020, 128u << 10, "ST 45PE10V6" },
{ 0x0C5820, 128u << 10, "5A32 (4k)" },
{ 0x0C6262, 128u << 10, "32B/3XH (4k)" },
};
static const spiflash_chip_t *spiflash_initialize(void) {
static spiflash_chip_t spiflash_chip = {
.id = 0xFFFFFFFF, /* filled at runtime */
.size = 128u << 10,
.name = "Unknown"
};
if (spiflash_chip.id != 0xFFFFFFFF)
return &spiflash_chip;
u32 id = PXI_DoCMD(PXICMD_NVRAM_ID, NULL, 0);
spiflash_chip.id = id;
for (unsigned i = 0; i < countof(spiflash_known_chips); i++) {
if (id == spiflash_known_chips[i].id) {
spiflash_chip = spiflash_known_chips[i];
break;
}
}
return &spiflash_chip;
}
u32 spiflash_size(void)
{
return spiflash_initialize()->size;
}
bool spiflash_read(u32 offset, u32 size, u8 *buf)
{
u32 total_size = spiflash_size();
if ((offset >= total_size) ||
(size > total_size) ||
(offset + size > total_size))
return false;
u32 *const dataBuffer = ARM_GetSHMEM()->dataBuffer.w;
u32 args[2];
while(size > 0) {
u32 blksz = min(size, SHMEM_BUFFER_SIZE);
args[0] = offset;
args[1] = blksz;
int result = PXI_DoCMD(PXICMD_NVRAM_READ, args, 2);
if (result != 0) {
ShowPrompt(false, "SPI flash read failed at offset\n0x%08lx size 0x%08lx (%d)", offset, blksz, result);
return false;
}
ARM_InvDC_Range(dataBuffer, blksz);
ARM_DSB();
memcpy(buf, dataBuffer, blksz);
buf += blksz;
size -= blksz;
offset += blksz;
}
return true;
}
bool spiflash_write(u32 offset, u32 size, const u8 *buf)
{
u32 total_size = spiflash_size();
if ((offset >= total_size) ||
(size > total_size) ||
(offset + size > total_size))
return false;
u32 *const dataBuffer = ARM_GetSHMEM()->dataBuffer.w;
u32 args[2];
while(size > 0) {
u32 blksz = min(size, SHMEM_BUFFER_SIZE);
args[0] = offset;
args[1] = blksz;
memcpy(dataBuffer, buf, blksz);
ARM_WbInvDC_Range(dataBuffer, blksz);
ARM_DSB();
int result = PXI_DoCMD(PXICMD_NVRAM_WRITE, args, 2);
if (result != 0) {
ShowPrompt(false, "SPI flash write failed at offset\n0x%08lx size 0x%08lx (%d)", offset, blksz, result);
return false;
}
buf += blksz;
size -= blksz;
offset += blksz;
}
return true;
}