mirror of
https://github.com/MeshEnvy/lobbs.git
synced 2026-11-04 03:52:26 +01:00
196 lines
5.8 KiB
C++
196 lines
5.8 KiB
C++
#include "LoFSQspi.h"
|
|
#include "configuration.h"
|
|
|
|
#if LOBBS_EXTRA_QSPI && defined(ARCH_NRF52)
|
|
#include "nrf.h"
|
|
#include "variant.h"
|
|
#include <Adafruit_LittleFS.h>
|
|
#include <Arduino.h>
|
|
#include <cstring>
|
|
#include <nrfx_qspi.h>
|
|
|
|
extern const uint32_t g_ADigitalPinMap[];
|
|
|
|
#include "../LoBBSBootTrace.h"
|
|
#include "LoBBSStackGuard.h"
|
|
|
|
static constexpr uint32_t LOFS_QSPI_BLOCK = 4096;
|
|
static constexpr uint32_t LOFS_QSPI_PAGE = 256;
|
|
static constexpr uint32_t LOFS_QSPI_BLOCK_COUNT = 512;
|
|
|
|
static uint8_t lofsQspiScratch[LOFS_QSPI_PAGE] __attribute__((aligned(4)));
|
|
static bool lofsQspiHwReady = false;
|
|
|
|
static uint8_t lofsQspiPin(uint32_t dPin)
|
|
{
|
|
return (uint8_t)g_ADigitalPinMap[dPin];
|
|
}
|
|
|
|
static nrfx_err_t lofsQspiCinstr(uint8_t op, nrf_qspi_cinstr_len_t len, void *rx)
|
|
{
|
|
nrf_qspi_cinstr_conf_t c = NRFX_QSPI_DEFAULT_CINSTR(op, len);
|
|
// IO2/IO3 are WP#/HOLD# when the quad-enable bit is clear; low HOLD# makes the flash ignore the command.
|
|
c.io2_level = true;
|
|
c.io3_level = true;
|
|
return nrfx_qspi_cinstr_xfer(&c, NULL, rx);
|
|
}
|
|
|
|
// nrfx_qspi_mem_busy_check() sends RDSR with HOLD# low, so the status byte floats.
|
|
static bool lofsQspiWaitReady()
|
|
{
|
|
uint8_t sr[4];
|
|
do {
|
|
if (lofsQspiCinstr(0x05, NRF_QSPI_CINSTR_LEN_2B, sr) != NRFX_SUCCESS)
|
|
return false;
|
|
} while (sr[0] & 0x01);
|
|
return true;
|
|
}
|
|
|
|
static bool lofsQspiReadJedec(uint8_t out[3])
|
|
{
|
|
// Flash state survives MCU resets on battery: release deep power-down, then software reset (66h/99h).
|
|
for (uint8_t op : {0xAB, 0x66, 0x99}) {
|
|
lofsQspiCinstr(op, NRF_QSPI_CINSTR_LEN_1B, NULL);
|
|
delayMicroseconds(50);
|
|
}
|
|
|
|
LOBBS_BOOT_STEP("qspi: jedec cinstr xfer");
|
|
nrfx_err_t err = lofsQspiCinstr(0x9F, NRF_QSPI_CINSTR_LEN_4B, out);
|
|
if (err != NRFX_SUCCESS) {
|
|
LOG_ERROR("LoFS QSPI JEDEC cinstr err %d", (int)err);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool lofsQspiInitHw()
|
|
{
|
|
if (lofsQspiHwReady)
|
|
return true;
|
|
|
|
LOBBS_BOOT_STEP("qspi: init hw enter");
|
|
|
|
#ifndef NRFX_QSPI_DEFAULT_CONFIG_IRQ_PRIORITY
|
|
#define NRFX_QSPI_DEFAULT_CONFIG_IRQ_PRIORITY 6
|
|
#endif
|
|
|
|
nrfx_qspi_config_t cfg =
|
|
NRFX_QSPI_DEFAULT_CONFIG(lofsQspiPin(PIN_QSPI_SCK), lofsQspiPin(PIN_QSPI_CS), lofsQspiPin(PIN_QSPI_IO0),
|
|
lofsQspiPin(PIN_QSPI_IO1), lofsQspiPin(PIN_QSPI_IO2), lofsQspiPin(PIN_QSPI_IO3));
|
|
// FASTREAD default: works without QE bit; READ2IO needs status-register setup on P25Q16.
|
|
cfg.phy_if.sck_freq = NRF_QSPI_FREQ_DIV8;
|
|
|
|
LOBBS_BOOT_STEP("qspi: nrfx_qspi_init");
|
|
nrfx_err_t err = nrfx_qspi_init(&cfg, NULL, NULL);
|
|
if (err == NRFX_ERROR_INVALID_STATE) {
|
|
lofsQspiHwReady = true;
|
|
} else if (err != NRFX_SUCCESS) {
|
|
LOG_ERROR("LoFS QSPI init failed: %d", (int)err);
|
|
return false;
|
|
} else {
|
|
lofsQspiHwReady = true;
|
|
}
|
|
|
|
uint8_t jedec[3];
|
|
if (!lofsQspiReadJedec(jedec)) {
|
|
LOG_ERROR("LoFS QSPI JEDEC read failed");
|
|
nrfx_qspi_uninit();
|
|
lofsQspiHwReady = false;
|
|
return false;
|
|
}
|
|
LOG_INFO("LoFS QSPI JEDEC %02x %02x %02x", jedec[0], jedec[1], jedec[2]);
|
|
if (jedec[0] != 0x85) {
|
|
LOG_ERROR("LoFS QSPI unexpected manufacturer 0x%02x", jedec[0]);
|
|
nrfx_qspi_uninit();
|
|
lofsQspiHwReady = false;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static int lofsQspiRead(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
|
|
{
|
|
(void)c;
|
|
uint32_t addr = block * LOFS_QSPI_BLOCK + off;
|
|
if (((uintptr_t)buffer & 3) == 0 && (size & 3) == 0) {
|
|
nrfx_err_t err = nrfx_qspi_read(buffer, size, addr);
|
|
return err == NRFX_SUCCESS ? 0 : -1;
|
|
}
|
|
uint8_t *dst = (uint8_t *)buffer;
|
|
while (size > 0) {
|
|
uint32_t chunk = size > LOFS_QSPI_PAGE ? LOFS_QSPI_PAGE : size;
|
|
uint32_t qchunk = (chunk + 3) & ~3u;
|
|
nrfx_err_t err = nrfx_qspi_read(lofsQspiScratch, qchunk, addr);
|
|
if (err != NRFX_SUCCESS)
|
|
return -1;
|
|
memcpy(dst, lofsQspiScratch, chunk);
|
|
dst += chunk;
|
|
addr += chunk;
|
|
size -= chunk;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int lofsQspiProg(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size)
|
|
{
|
|
(void)c;
|
|
uint32_t addr = block * LOFS_QSPI_BLOCK + off;
|
|
if (((uintptr_t)buffer & 3) == 0 && (size & 3) == 0) {
|
|
nrfx_err_t err = nrfx_qspi_write(buffer, size, addr);
|
|
lofsQspiWaitReady();
|
|
return err == NRFX_SUCCESS ? 0 : -1;
|
|
}
|
|
const uint8_t *src = (const uint8_t *)buffer;
|
|
while (size > 0) {
|
|
uint32_t chunk = size > LOFS_QSPI_PAGE ? LOFS_QSPI_PAGE : size;
|
|
uint32_t qchunk = (chunk + 3) & ~3u;
|
|
memcpy(lofsQspiScratch, src, chunk);
|
|
for (uint32_t i = chunk; i < qchunk; i++)
|
|
lofsQspiScratch[i] = 0xFF;
|
|
nrfx_err_t err = nrfx_qspi_write(lofsQspiScratch, qchunk, addr);
|
|
if (err != NRFX_SUCCESS)
|
|
return -1;
|
|
src += chunk;
|
|
addr += chunk;
|
|
size -= chunk;
|
|
}
|
|
lofsQspiWaitReady();
|
|
return 0;
|
|
}
|
|
|
|
static int lofsQspiErase(const struct lfs_config *c, lfs_block_t block)
|
|
{
|
|
(void)c;
|
|
uint32_t addr = block * LOFS_QSPI_BLOCK;
|
|
nrfx_err_t err = nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_4KB, addr);
|
|
lofsQspiWaitReady();
|
|
return err == NRFX_SUCCESS ? 0 : -1;
|
|
}
|
|
|
|
static int lofsQspiSync(const struct lfs_config *c)
|
|
{
|
|
(void)c;
|
|
lofsQspiWaitReady();
|
|
return 0;
|
|
}
|
|
|
|
bool lofsQspiConfig(struct lfs_config &cfg)
|
|
{
|
|
LOBBS_BOOT_STEP("qspi: lofsQspiConfig");
|
|
if (!lofsQspiInitHw())
|
|
return false;
|
|
cfg.context = NULL;
|
|
cfg.read = lofsQspiRead;
|
|
cfg.prog = lofsQspiProg;
|
|
cfg.erase = lofsQspiErase;
|
|
cfg.sync = lofsQspiSync;
|
|
cfg.read_size = LOFS_QSPI_PAGE;
|
|
cfg.prog_size = LOFS_QSPI_PAGE;
|
|
cfg.block_size = LOFS_QSPI_BLOCK;
|
|
cfg.block_count = LOFS_QSPI_BLOCK_COUNT;
|
|
cfg.lookahead = 512;
|
|
return true;
|
|
}
|
|
|
|
#endif
|