Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions src/main/drivers/flash_w25n.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ static bool couldBeBusy = false;

static timeMs_t timeoutAt = 0;

// The page held in the device's internal data buffer, or UINT32_MAX if its contents are unknown
static uint32_t currentPage = UINT32_MAX;

static bool w25n_waitForReadyInternal(void);

static void w25n_setTimeout(timeMs_t timeoutMillis)
Expand Down Expand Up @@ -216,9 +219,20 @@ static bool w25n_waitForReadyInternal(void)
return true;
}

/**
* Wait for the device to become ready.
*
* A timeout of zero means "wait for the deadline the pending operation has already armed",
* which is how flashPartitionErase() waits for a block erase to complete (see also
* m25p16_waitForReady). Arming a fresh zero length timeout instead would replace the erase
* timeout with a deadline that has already expired, and the wait would give up immediately.
*/
bool w25n_waitForReady(timeMs_t timeoutMillis)
{
w25n_setTimeout(timeoutMillis);
if (timeoutMillis > 0) {
w25n_setTimeout(timeoutMillis);
}

return w25n_waitForReadyInternal();
}

Expand Down Expand Up @@ -300,10 +314,19 @@ bool w25n_detect(uint32_t chipID)
*/
void w25n_eraseSector(uint32_t address)
{
w25n_waitForReadyInternal();
// A device that is still busy ignores both the write enable and the erase instruction, so
// issuing them anyway would leave the block unerased without any sign of it. Give up on this
// block instead; the caller sees the result when it reads the flash back.
if (!w25n_waitForReadyInternal()) {
return;
}

w25n_writeEnable();
w25n_performCommandWithPageAddress(W25N_INSTRUCTION_BLOCK_ERASE, W25N_LINEAR_TO_PAGE(address));
w25n_setTimeout(W25N_TIMEOUT_BLOCK_ERASE_MS);

// The data buffer may still hold a page of the block that is being erased
currentPage = UINT32_MAX;
}

// W25N does not support full chip erase.
Expand All @@ -313,6 +336,9 @@ void w25n_eraseCompletely(void)
for (uint32_t block = 0; block < geometry.sectors; block++) {
w25n_eraseSector(W25N_BLOCK_TO_LINEAR(block));
}

// Let the last block finish, so that the device is readable once the erase returns
w25n_waitForReadyInternal();
}

static void w25n_programDataLoad(uint16_t columnAddress, const uint8_t *data, int length)
Expand Down Expand Up @@ -377,7 +403,6 @@ bool bufferDirty = false;
bool isProgramming = false;
static uint32_t programStartAddress;
static uint32_t programLoadAddress;
static uint32_t currentPage = UINT32_MAX;

void w25n_pageProgramBegin(uint32_t address)
{
Expand Down
18 changes: 17 additions & 1 deletion src/main/io/flashfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,25 @@ static void flashfsSetTailAddress(uint32_t address)

void flashfsEraseCompletely(void)
{
// Drain the driver's page cache before erasing; the free-space scan reuses the chip buffer.
flashFlush();
flashPartitionErase(flashPartition);
flashfsClearBuffer();
flashfsSetTailAddress(0);

/* Assuming the erase succeeded would report an empty device from RAM while the flash still
* holds the old log, which only becomes apparent after the next reboot. Locate the start of
* the free space on the chip instead, exactly as flashfsInit() does at boot.
*
* A chip that is still erasing cannot be examined, as reads would time out and the device
* would be taken for full. That is the case for a NOR chip erased with a single bulk erase
* instruction, which completes long after this function returns; there the erase is assumed
* to succeed as before, and the next boot corrects the offset if it did not.
*/
if (flashIsReady()) {
flashfsSetTailAddress(flashfsIdentifyStartOfFreeSpace());
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
} else {
flashfsSetTailAddress(0);
}
}

void flashfsClose(void)
Expand Down