2020-07-18 20:25:34 -03:00
|
|
|
/*
|
|
|
|
|
* This file is part of GodMode9
|
|
|
|
|
* Copyright (C) 2019 Wolfvak
|
|
|
|
|
*
|
|
|
|
|
* 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 2 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <types.h>
|
|
|
|
|
|
|
|
|
|
#include <spi.h>
|
2026-02-21 20:51:42 -03:00
|
|
|
#include "arm/timer.h"
|
2020-07-18 20:25:34 -03:00
|
|
|
#include "hw/nvram.h"
|
|
|
|
|
|
|
|
|
|
// returns manuf id, memory type and size
|
|
|
|
|
// size = (1 << id[2]) ?
|
|
|
|
|
// apparently unreliable on some Sanyo chips?
|
|
|
|
|
#define CMD_RDID 0x9F
|
|
|
|
|
|
|
|
|
|
#define CMD_READ 0x03
|
|
|
|
|
#define CMD_WREN 0x06
|
|
|
|
|
#define CMD_WRDI 0x04
|
2026-02-21 20:51:42 -03:00
|
|
|
#define CMD_WRITE 0x0A
|
2020-07-18 20:25:34 -03:00
|
|
|
|
|
|
|
|
#define CMD_RDSR 0x05
|
|
|
|
|
|
2026-02-21 20:51:42 -03:00
|
|
|
#define NVRAM_SR_WIP BIT(0) // work in progress / busy
|
|
|
|
|
#define NVRAM_SR_WEL BIT(1) // write enable latch
|
|
|
|
|
|
2020-07-18 20:25:34 -03:00
|
|
|
#define CMD_DPD 0xB9 // deep power down
|
|
|
|
|
#define CMD_RDP 0xAB // release from deep power down
|
|
|
|
|
|
2026-02-21 20:51:42 -03:00
|
|
|
#define NVRAM_PAGE_SIZE 0x100 // 256 byte pages
|
|
|
|
|
#define NVRAM_ADDR_MASK 0xFFFFFF // 24bit address
|
|
|
|
|
#define NVRAM_ADDR_MAX (NVRAM_ADDR_MASK + 1)
|
|
|
|
|
|
2020-07-18 20:25:34 -03:00
|
|
|
static u32 NVRAM_SendStatusCommand(u32 cmd, u32 width)
|
|
|
|
|
{
|
|
|
|
|
u32 ret;
|
|
|
|
|
SPI_XferInfo xfer[2];
|
|
|
|
|
|
|
|
|
|
xfer[0].buf = &cmd;
|
|
|
|
|
xfer[0].len = 1;
|
|
|
|
|
xfer[0].read = false;
|
|
|
|
|
|
|
|
|
|
xfer[1].buf = &ret;
|
|
|
|
|
xfer[1].len = width;
|
|
|
|
|
xfer[1].read = true;
|
|
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
SPI_DoXfer(SPI_DEV_NVRAM, xfer, 2, true);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 20:51:42 -03:00
|
|
|
static void NVRAM_SetWriteEnable(bool write_en)
|
2020-07-18 20:25:34 -03:00
|
|
|
{
|
2026-02-21 20:51:42 -03:00
|
|
|
u8 cmd = write_en ? CMD_WREN : CMD_WRDI;
|
|
|
|
|
SPI_XferInfo xfer;
|
|
|
|
|
xfer.buf = &cmd;
|
|
|
|
|
xfer.len = 1;
|
|
|
|
|
xfer.read = false;
|
|
|
|
|
SPI_DoXfer(SPI_DEV_NVRAM, &xfer, 1, true);
|
2020-07-18 20:25:34 -03:00
|
|
|
}
|
|
|
|
|
|
2026-02-21 20:51:42 -03:00
|
|
|
u32 NVRAM_Status(void)
|
2020-07-18 20:25:34 -03:00
|
|
|
{
|
2026-02-21 20:51:42 -03:00
|
|
|
return NVRAM_SendStatusCommand(CMD_RDSR, 1);
|
2020-07-18 20:25:34 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NVRAM_DeepStandby(void)
|
|
|
|
|
{
|
|
|
|
|
NVRAM_SendStatusCommand(CMD_DPD, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NVRAM_Wakeup(void)
|
|
|
|
|
{
|
|
|
|
|
NVRAM_SendStatusCommand(CMD_RDP, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 20:51:42 -03:00
|
|
|
u32 NVRAM_ReadID(void)
|
|
|
|
|
{
|
|
|
|
|
return NVRAM_SendStatusCommand(CMD_RDID, 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int NVRAM_Read(u32 address, u32 *buffer, u32 len)
|
2020-07-18 20:25:34 -03:00
|
|
|
{
|
|
|
|
|
SPI_XferInfo xfer[2];
|
|
|
|
|
u32 cmd;
|
|
|
|
|
|
2026-02-21 20:51:42 -03:00
|
|
|
if (address >= NVRAM_ADDR_MAX || len > NVRAM_ADDR_MAX || (address + len) > NVRAM_ADDR_MAX)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2020-07-18 20:25:34 -03:00
|
|
|
cmd = __builtin_bswap32(address) | CMD_READ;
|
|
|
|
|
|
|
|
|
|
xfer[0].buf = &cmd;
|
|
|
|
|
xfer[0].len = 4;
|
|
|
|
|
xfer[0].read = false;
|
|
|
|
|
|
|
|
|
|
xfer[1].buf = buffer;
|
|
|
|
|
xfer[1].len = len;
|
|
|
|
|
xfer[1].read = true;
|
|
|
|
|
|
|
|
|
|
SPI_DoXfer(SPI_DEV_NVRAM, xfer, 2, true);
|
2026-02-21 20:51:42 -03:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int NVRAM_WritePage(u32 address, const u32 *buffer, u32 len)
|
|
|
|
|
{
|
|
|
|
|
SPI_XferInfo xfer[2];
|
|
|
|
|
u32 cmd, i;
|
|
|
|
|
|
|
|
|
|
if (address >= NVRAM_ADDR_MAX || len > NVRAM_PAGE_SIZE || (address + len) > NVRAM_ADDR_MAX)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
cmd = __builtin_bswap32(address) | CMD_WRITE;
|
|
|
|
|
|
|
|
|
|
xfer[0].buf = &cmd;
|
|
|
|
|
xfer[0].len = 4;
|
|
|
|
|
xfer[0].read = false;
|
|
|
|
|
|
|
|
|
|
xfer[1].buf = (void*)buffer;
|
|
|
|
|
xfer[1].len = len;
|
|
|
|
|
xfer[1].read = false;
|
|
|
|
|
|
|
|
|
|
// enable the write latch
|
|
|
|
|
NVRAM_SetWriteEnable(true);
|
|
|
|
|
// make sure it's enabled
|
|
|
|
|
for (i = 0; i <= 1000; i++) {
|
|
|
|
|
if (i == 1000) return -2;
|
|
|
|
|
if (NVRAM_Status() & NVRAM_SR_WEL) break;
|
|
|
|
|
TIMER_WaitMS(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// do the write transfer
|
|
|
|
|
SPI_DoXfer(SPI_DEV_NVRAM, xfer, 2, true);
|
|
|
|
|
|
|
|
|
|
// wait until it's done, disable the write latch
|
|
|
|
|
for (i = 0; i <= 1000; i++) {
|
|
|
|
|
if (i == 1000) return -3;
|
|
|
|
|
if (!(NVRAM_Status() & NVRAM_SR_WIP)) break;
|
|
|
|
|
TIMER_WaitMS(1);
|
|
|
|
|
}
|
|
|
|
|
NVRAM_SetWriteEnable(false);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int NVRAM_Write(u32 address, const u32 *buffer, u32 len)
|
|
|
|
|
{
|
|
|
|
|
while(len > 0) {
|
|
|
|
|
u32 blksz = len < NVRAM_PAGE_SIZE ? len : NVRAM_PAGE_SIZE;
|
|
|
|
|
|
|
|
|
|
int result = NVRAM_WritePage(address, buffer, blksz);
|
|
|
|
|
if (result != 0)
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
address += blksz;
|
|
|
|
|
buffer += (blksz / 4);
|
|
|
|
|
len -= blksz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
2020-07-18 20:25:34 -03:00
|
|
|
}
|