aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builddefs/common_features.mk9
-rw-r--r--drivers/flash/flash.h126
-rw-r--r--drivers/flash/flash_spi.c89
-rw-r--r--drivers/flash/flash_spi.h31
-rw-r--r--drivers/wear_leveling/wear_leveling_flash_spi.c4
5 files changed, 182 insertions, 77 deletions
diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk
index 68f9a1dd08..498614dd26 100644
--- a/builddefs/common_features.mk
+++ b/builddefs/common_features.mk
@@ -282,18 +282,17 @@ ifneq ($(strip $(WEAR_LEVELING_DRIVER)),none)
endif
endif
-VALID_FLASH_DRIVER_TYPES := spi
+VALID_FLASH_DRIVER_TYPES := spi custom
FLASH_DRIVER ?= none
ifneq ($(strip $(FLASH_DRIVER)), none)
ifeq ($(filter $(FLASH_DRIVER),$(VALID_FLASH_DRIVER_TYPES)),)
$(call CATASTROPHIC_ERROR,Invalid FLASH_DRIVER,FLASH_DRIVER="$(FLASH_DRIVER)" is not a valid flash driver)
else
- OPT_DEFS += -DFLASH_ENABLE
+ OPT_DEFS += -DFLASH_ENABLE -DFLASH_DRIVER -DFLASH_DRIVER_$(strip $(shell echo $(FLASH_DRIVER) | tr '[:lower:]' '[:upper:]'))
+ COMMON_VPATH += $(DRIVER_PATH)/flash
ifeq ($(strip $(FLASH_DRIVER)),spi)
- SPI_DRIVER_REQUIRED = yes
- OPT_DEFS += -DFLASH_DRIVER -DFLASH_SPI
- COMMON_VPATH += $(DRIVER_PATH)/flash
SRC += flash_spi.c
+ SPI_DRIVER_REQUIRED = yes
endif
endif
endif
diff --git a/drivers/flash/flash.h b/drivers/flash/flash.h
new file mode 100644
index 0000000000..4d62475139
--- /dev/null
+++ b/drivers/flash/flash.h
@@ -0,0 +1,126 @@
+// Copyright 2024 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+/**
+ * @brief The status of a flash operation.
+ */
+enum {
+ FLASH_STATUS_SUCCESS = 0, //< The operation completed successfully.
+ FLASH_STATUS_ERROR = -1, //< An error occurred during the operation.
+ FLASH_STATUS_TIMEOUT = -2, //< The operation timed out.
+ FLASH_STATUS_BAD_ADDRESS = -3, //< The address is out of bounds.
+ FLASH_STATUS_BUSY = -4, //< The flash is busy.
+};
+
+/**
+ * @brief The status of a flash operation.
+ */
+typedef int16_t flash_status_t;
+
+/**
+ * @brief Initializes the flash driver.
+ *
+ * This function initializes the flash driver and prepares it for use.
+ * It should be called before any other flash-related functions are used.
+ */
+void flash_init(void);
+
+/**
+ * @brief Checks if the flash is busy.
+ *
+ * This function checks if the flash is currently busy with an operation.
+ *
+ * @return FLASH_STATUS_SUCCESS if the flash is not busy, FLASH_STATUS_BUSY if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.
+ */
+flash_status_t flash_is_busy(void);
+
+/**
+ * @brief Initiates a chip erase operation.
+ *
+ * This function does not wait for the flash to become ready.
+ *
+ * @return FLASH_STATUS_SUCCESS if the erase command was successfully sent, FLASH_STATUS_TIMEOUT if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.
+ */
+flash_status_t flash_begin_erase_chip(void);
+
+/**
+ * @brief Waits for the chip erase operation to complete.
+ *
+ * This function waits for the chip erase operation to complete.
+ *
+ * @return FLASH_STATUS_SUCCESS if the chip erase operation completed successfully, FLASH_STATUS_TIMEOUT if the flash was still busy, or FLASH_STATUS_ERROR if an error occurred.
+ */
+flash_status_t flash_wait_erase_chip(void);
+
+/**
+ * @brief Erases the entire flash memory chip.
+ *
+ * This function initiates an erase operation to erase the entire flash memory chip.
+ * It waits for the operation to complete.
+ *
+ * @return FLASH_STATUS_SUCCESS if the erase was successfully executed, FLASH_STATUS_TIMEOUT if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.
+ */
+flash_status_t flash_erase_chip(void);
+
+/**
+ * @brief Erases a block of flash memory.
+ *
+ * This function initiates an erase operation to erase a block of flash memory.
+ * It waits for the operation to complete.
+ *
+ * @param addr The address of the block to erase.
+ *
+ * @return FLASH_STATUS_SUCCESS if the erase was successfully executed, FLASH_STATUS_TIMEOUT if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.
+ */
+flash_status_t flash_erase_block(uint32_t addr);
+
+/**
+ * @brief Erases a sector of flash memory.
+ *
+ * This function initiates an erase operation to erase a sector of flash memory.
+ * It waits for the operation to complete.
+ *
+ * @param addr The address of the sector to erase.
+ *
+ * @return FLASH_STATUS_SUCCESS if the erase was successfully executed, FLASH_STATUS_TIMEOUT if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.
+ */
+flash_status_t flash_erase_sector(uint32_t addr);
+
+/**
+ * @brief Reads a range of flash memory.
+ *
+ * This function reads a range of flash memory into a buffer.
+ *
+ * @param addr The address of the range to read.
+ * @param buf A pointer to the buffer to read the range into.
+ * @param len The length of the range to read.
+ *
+ * @return FLASH_STATUS_SUCCESS if the range was successfully read, FLASH_STATUS_BAD_ADDRESS if the address is out of bounds, FLASH_STATUS_TIMEOUT if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.
+ */
+flash_status_t flash_read_range(uint32_t addr, void *buf, size_t len);
+
+/**
+ * @brief Writes a range of flash memory.
+ *
+ * This function writes a range of flash memory from a buffer.
+ *
+ * @param addr The address of the range to write.
+ * @param buf A pointer to the buffer to write to the range.
+ * @param len The length of the range to write.
+ *
+ * @return FLASH_STATUS_SUCCESS if the range was successfully written, FLASH_STATUS_BAD_ADDRESS if the address is out of bounds, FLASH_STATUS_TIMEOUT if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.
+ */
+flash_status_t flash_write_range(uint32_t addr, const void *buf, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/drivers/flash/flash_spi.c b/drivers/flash/flash_spi.c
index 0c0eb8a99e..7226773ff4 100644
--- a/drivers/flash/flash_spi.c
+++ b/drivers/flash/flash_spi.c
@@ -1,22 +1,10 @@
-/*
-Copyright (C) 2021 Westberry Technology (ChangZhou) Corp., Ltd
-
-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/>.
-*/
+// Copyright 2021 Westberry Technology (ChangZhou) Corp., Ltd
+// Copyright 2024 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <string.h>
+#include "flash.h"
#include "util.h"
#include "wait.h"
#include "debug.h"
@@ -69,33 +57,43 @@ static bool spi_flash_start(void) {
return spi_start(EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN, EXTERNAL_FLASH_SPI_LSBFIRST, EXTERNAL_FLASH_SPI_MODE, EXTERNAL_FLASH_SPI_CLOCK_DIVISOR);
}
-static flash_status_t spi_flash_wait_while_busy(void) {
- uint32_t deadline = timer_read32() + EXTERNAL_FLASH_SPI_TIMEOUT;
+static flash_status_t spi_flash_wait_while_busy_multiplier(int multiplier) {
flash_status_t response = FLASH_STATUS_SUCCESS;
- uint8_t retval;
-
+ uint32_t deadline = timer_read32() + ((EXTERNAL_FLASH_SPI_TIMEOUT)*multiplier);
do {
- bool res = spi_flash_start();
- if (!res) {
- dprint("Failed to start SPI! [spi flash wait while busy]\n");
- return FLASH_STATUS_ERROR;
- }
-
- spi_write(FLASH_CMD_RDSR);
-
- retval = (uint8_t)spi_read();
-
- spi_stop();
-
if (timer_read32() >= deadline) {
response = FLASH_STATUS_TIMEOUT;
break;
}
- } while (retval & FLASH_FLAG_WIP);
+ response = flash_is_busy();
+ } while (response == FLASH_STATUS_BUSY);
return response;
}
+static flash_status_t spi_flash_wait_while_busy(void) {
+ return spi_flash_wait_while_busy_multiplier(1);
+}
+
+flash_status_t flash_is_busy(void) {
+ bool res = spi_flash_start();
+ if (!res) {
+ dprint("Failed to start SPI! [spi flash wait while busy]\n");
+ return FLASH_STATUS_ERROR;
+ }
+
+ spi_write(FLASH_CMD_RDSR);
+ spi_status_t status = spi_read();
+ spi_stop();
+
+ if (status < 0) {
+ return status;
+ }
+
+ uint8_t sr = (uint8_t)status;
+ return (sr & FLASH_FLAG_WIP) ? FLASH_STATUS_BUSY : FLASH_STATUS_SUCCESS;
+}
+
static flash_status_t spi_flash_write_enable(void) {
bool res = spi_flash_start();
if (!res) {
@@ -104,7 +102,6 @@ static flash_status_t spi_flash_write_enable(void) {
}
spi_write(FLASH_CMD_WREN);
-
spi_stop();
return FLASH_STATUS_SUCCESS;
@@ -118,7 +115,6 @@ static flash_status_t spi_flash_write_disable(void) {
}
spi_write(FLASH_CMD_WRDI);
-
spi_stop();
return FLASH_STATUS_SUCCESS;
@@ -166,7 +162,7 @@ void flash_init(void) {
spi_init();
}
-flash_status_t flash_erase_chip(void) {
+flash_status_t flash_begin_erase_chip(void) {
flash_status_t response = FLASH_STATUS_SUCCESS;
/* Wait for the write-in-progress bit to be cleared. */
@@ -191,17 +187,28 @@ flash_status_t flash_erase_chip(void) {
}
spi_write(FLASH_CMD_CE);
spi_stop();
+ return FLASH_STATUS_SUCCESS;
+}
- /* Wait for the write-in-progress bit to be cleared.*/
- response = spi_flash_wait_while_busy();
+flash_status_t flash_wait_erase_chip(void) {
+ flash_status_t response = spi_flash_wait_while_busy_multiplier(250); // Chip erase can take a long time, wait 250x the usual timeout
if (response != FLASH_STATUS_SUCCESS) {
dprint("Failed to check WIP flag! [spi flash erase chip]\n");
return response;
}
-
return response;
}
+flash_status_t flash_erase_chip(void) {
+ flash_status_t response = flash_begin_erase_chip();
+ if (response != FLASH_STATUS_SUCCESS) {
+ dprint("Failed to begin erase chip! [spi flash erase chip]\n");
+ return response;
+ }
+
+ return flash_wait_erase_chip();
+}
+
flash_status_t flash_erase_sector(uint32_t addr) {
flash_status_t response = FLASH_STATUS_SUCCESS;
@@ -282,7 +289,7 @@ flash_status_t flash_erase_block(uint32_t addr) {
return response;
}
-flash_status_t flash_read_block(uint32_t addr, void *buf, size_t len) {
+flash_status_t flash_read_range(uint32_t addr, void *buf, size_t len) {
flash_status_t response = FLASH_STATUS_SUCCESS;
uint8_t * read_buf = (uint8_t *)buf;
@@ -313,7 +320,7 @@ flash_status_t flash_read_block(uint32_t addr, void *buf, size_t len) {
return response;
}
-flash_status_t flash_write_block(uint32_t addr, const void *buf, size_t len) {
+flash_status_t flash_write_range(uint32_t addr, const void *buf, size_t len) {
flash_status_t response = FLASH_STATUS_SUCCESS;
uint8_t * write_buf = (uint8_t *)buf;
diff --git a/drivers/flash/flash_spi.h b/drivers/flash/flash_spi.h
index 87460fc210..7a979daf0f 100644
--- a/drivers/flash/flash_spi.h
+++ b/drivers/flash/flash_spi.h
@@ -17,6 +17,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#pragma once
+#include "flash.h"
+
/* All the following default configurations are based on MX25L4006E Nor FLASH. */
/*
@@ -105,32 +107,3 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
The page count of the FLASH, calculated by total FLASH size and page size.
*/
#define EXTERNAL_FLASH_PAGE_COUNT ((EXTERNAL_FLASH_SIZE) / (EXTERNAL_FLASH_PAGE_SIZE))
-
-typedef int16_t flash_status_t;
-
-#define FLASH_STATUS_SUCCESS (0)
-#define FLASH_STATUS_ERROR (-1)
-#define FLASH_STATUS_TIMEOUT (-2)
-#define FLASH_STATUS_BAD_ADDRESS (-3)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-
-void flash_init(void);
-
-flash_status_t flash_erase_chip(void);
-
-flash_status_t flash_erase_block(uint32_t addr);
-
-flash_status_t flash_erase_sector(uint32_t addr);
-
-flash_status_t flash_read_block(uint32_t addr, void *buf, size_t len);
-
-flash_status_t flash_write_block(uint32_t addr, const void *buf, size_t len);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/drivers/wear_leveling/wear_leveling_flash_spi.c b/drivers/wear_leveling/wear_leveling_flash_spi.c
index 6191f8bf09..304aed1641 100644
--- a/drivers/wear_leveling/wear_leveling_flash_spi.c
+++ b/drivers/wear_leveling/wear_leveling_flash_spi.c
@@ -58,7 +58,7 @@ bool backing_store_read(uint32_t address, backing_store_int_t *value) {
bool backing_store_read_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {
bs_dprintf("Read ");
uint32_t offset = (WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_OFFSET) * (EXTERNAL_FLASH_BLOCK_SIZE) + address;
- flash_status_t status = flash_read_block(offset, values, sizeof(backing_store_int_t) * item_count);
+ flash_status_t status = flash_read_range(offset, values, sizeof(backing_store_int_t) * item_count);
if (status == FLASH_STATUS_SUCCESS) {
for (size_t i = 0; i < item_count; ++i) {
values[i] = ~values[i];
@@ -88,7 +88,7 @@ bool backing_store_write_bulk(uint32_t address, backing_store_int_t *values, siz
}
// Write out the block
- if (flash_write_block(offset, temp, sizeof(backing_store_int_t) * this_loop) != FLASH_STATUS_SUCCESS) {
+ if (flash_write_range(offset, temp, sizeof(backing_store_int_t) * this_loop) != FLASH_STATUS_SUCCESS) {
return false;
}