Explorar o código

SPI flash API cleanup, add async erase capability. (#23894)

Nick Brassel %!s(int64=2) %!d(string=hai) anos
pai
achega
7bc3eef8cc

+ 4 - 5
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

+ 126 - 0
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

+ 48 - 41
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;
 

+ 2 - 29
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

+ 2 - 2
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;
         }